@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,1076 @@
|
|
|
1
|
+
# State Management Anti-Patterns
|
|
2
|
+
|
|
3
|
+
> Domain: Code | Applies to: All frameworks and languages with stateful logic
|
|
4
|
+
> Severity range: Medium to Critical | Module version: 1.0.0
|
|
5
|
+
|
|
6
|
+
State management is the single largest source of bugs in modern applications. Every
|
|
7
|
+
user-visible glitch -- a stale form, a phantom spinner, a lost edit -- traces back to
|
|
8
|
+
state that was duplicated, desynchronized, mutated behind someone's back, or simply
|
|
9
|
+
never modeled correctly. This module catalogs 20 recurring anti-patterns, drawn from
|
|
10
|
+
production incidents, framework issue trackers, and community post-mortems across
|
|
11
|
+
React, Angular, Vue, Flutter, SwiftUI, and backend systems in Java, Python, and Go.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Anti-Pattern 1: Global Mutable Singleton
|
|
16
|
+
|
|
17
|
+
**Also known as:** God Object State, Ambient State, Service Locator State
|
|
18
|
+
|
|
19
|
+
**Frequency:** Very High | **Severity:** High | **Detection difficulty:** Medium
|
|
20
|
+
|
|
21
|
+
### What it looks like
|
|
22
|
+
|
|
23
|
+
A singleton class or module-level variable holds mutable application state accessible
|
|
24
|
+
from anywhere. In Java, a `SessionManager.getInstance().currentUser` mutated by
|
|
25
|
+
multiple threads. In Python, a module-level dictionary imported across files. In
|
|
26
|
+
JavaScript, a plain object on `window` or a module-scope `let` that multiple
|
|
27
|
+
components read and write.
|
|
28
|
+
|
|
29
|
+
### Why developers do it
|
|
30
|
+
|
|
31
|
+
It is the shortest path from "I need this data everywhere" to working code. Frameworks
|
|
32
|
+
historically encouraged it (e.g., AngularJS services, Android Application subclass).
|
|
33
|
+
Junior developers copy patterns from tutorials that skip dependency injection setup.
|
|
34
|
+
|
|
35
|
+
### What goes wrong
|
|
36
|
+
|
|
37
|
+
- **Concurrency bugs.** Two threads or two async callbacks mutate the same field; the
|
|
38
|
+
last writer wins silently. An IEEE study (2020) found classes utilizing mutable
|
|
39
|
+
global state are measurably more defect-prone.
|
|
40
|
+
- **Test pollution.** Test A mutates the singleton; test B reads stale values. Tests
|
|
41
|
+
pass individually, fail when run together. Ordering-dependent test suites follow.
|
|
42
|
+
- **Hidden coupling.** Any file can import and mutate the singleton, creating invisible
|
|
43
|
+
dependency graphs that resist refactoring.
|
|
44
|
+
- **Name collisions.** Third-party libraries using the same global key overwrite your
|
|
45
|
+
state silently.
|
|
46
|
+
|
|
47
|
+
### The fix
|
|
48
|
+
|
|
49
|
+
Use dependency injection to pass state explicitly. In frontend apps, use scoped state
|
|
50
|
+
management (React Context, provider trees, Zustand stores). In backend systems, pass
|
|
51
|
+
state through function parameters or request-scoped containers. If a singleton is
|
|
52
|
+
truly needed, make it immutable and expose mutation only through controlled methods
|
|
53
|
+
with synchronization.
|
|
54
|
+
|
|
55
|
+
### Detection rule
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Grep for module-level `let`/`var` assignments that are reassigned in multiple files.
|
|
59
|
+
Flag singleton classes with public mutable fields. In Java, flag `static` non-final
|
|
60
|
+
fields. In Python, flag module-scope mutable assignments imported by >2 modules.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Anti-Pattern 2: State Duplication
|
|
66
|
+
|
|
67
|
+
**Also known as:** Denormalized State, Copy-Paste State, Dual Source of Truth
|
|
68
|
+
|
|
69
|
+
**Frequency:** Very High | **Severity:** High | **Detection difficulty:** Medium
|
|
70
|
+
|
|
71
|
+
### What it looks like
|
|
72
|
+
|
|
73
|
+
The same business entity exists in two or more places in the state tree. A user's
|
|
74
|
+
name lives in `authStore.user.name` and also in `profileStore.displayName`. An order
|
|
75
|
+
total is stored both on the order object and in a summary widget's local state. When
|
|
76
|
+
one copy updates, the other does not.
|
|
77
|
+
|
|
78
|
+
### Why developers do it
|
|
79
|
+
|
|
80
|
+
Different features are built by different teams or at different times. Each feature
|
|
81
|
+
grabs a snapshot of the data it needs. "I'll just copy it here so I don't have to
|
|
82
|
+
prop-drill." Denormalization feels like a performance optimization.
|
|
83
|
+
|
|
84
|
+
### What goes wrong
|
|
85
|
+
|
|
86
|
+
- **Drift.** User updates their name in settings; the header still shows the old name
|
|
87
|
+
until full page reload.
|
|
88
|
+
- **Conflicting writes.** Two components update their local copy; the last write to
|
|
89
|
+
the server wins, but the UI shows a mix of old and new data.
|
|
90
|
+
- **Reducer complexity explodes.** Every action that changes a shared entity must now
|
|
91
|
+
update N locations, or you need cross-slice synchronization logic.
|
|
92
|
+
|
|
93
|
+
### The fix
|
|
94
|
+
|
|
95
|
+
Establish a single source of truth for each entity. Reference entities by ID and look
|
|
96
|
+
them up from a single normalized store. Use selectors or computed properties to derive
|
|
97
|
+
views. If denormalization is needed for performance, treat it as a read-only cache with
|
|
98
|
+
explicit invalidation.
|
|
99
|
+
|
|
100
|
+
### Detection rule
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
In Redux/Zustand: flag identical entity types stored under multiple top-level keys.
|
|
104
|
+
In any codebase: search for the same API response being spread into multiple state
|
|
105
|
+
locations without a shared reference.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Anti-Pattern 3: Derived State Stored Separately
|
|
111
|
+
|
|
112
|
+
**Also known as:** Computed State in Store, The Synchronization Tax, Stale Derivation
|
|
113
|
+
|
|
114
|
+
**Frequency:** Very High | **Severity:** Medium | **Detection difficulty:** Low
|
|
115
|
+
|
|
116
|
+
### What it looks like
|
|
117
|
+
|
|
118
|
+
A value that can be computed from existing state is instead stored as its own state
|
|
119
|
+
variable. Example: storing both `items` (an array) and `itemCount` (a number). Storing
|
|
120
|
+
both `cart` and `cartTotal`. Using `useEffect` to update `fullName` whenever
|
|
121
|
+
`firstName` or `lastName` changes.
|
|
122
|
+
|
|
123
|
+
### Why developers do it
|
|
124
|
+
|
|
125
|
+
It feels like an optimization -- "why recompute every render?" Developers coming from
|
|
126
|
+
imperative backgrounds think in terms of cache variables. React's `useEffect` makes it
|
|
127
|
+
easy to set up "sync" logic that stores derived values.
|
|
128
|
+
|
|
129
|
+
### What goes wrong
|
|
130
|
+
|
|
131
|
+
- **Synchronization bugs.** The derived value lags the source by one render cycle, or
|
|
132
|
+
the `useEffect` dependency array is incomplete, so it never updates.
|
|
133
|
+
- **Double renders.** Setting state inside `useEffect` triggers an extra render cycle
|
|
134
|
+
that is completely unnecessary.
|
|
135
|
+
- **Bug surface area.** Every stored derivation is another variable that can fall out
|
|
136
|
+
of sync, creating "impossible" UI states.
|
|
137
|
+
|
|
138
|
+
### The fix
|
|
139
|
+
|
|
140
|
+
Compute derived values inline during render. For expensive computations, use
|
|
141
|
+
memoization (`useMemo`, `computed()` in Vue/MobX, `createSelector` in Redux). The
|
|
142
|
+
principle: "Don't sync state -- derive it." Store the minimal canonical state and
|
|
143
|
+
compute everything else.
|
|
144
|
+
|
|
145
|
+
### Detection rule
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
Flag useEffect calls where the body contains only a setState call derived from other
|
|
149
|
+
state/props. Flag state variables whose names are prefixes/suffixes of other state
|
|
150
|
+
(e.g., items/itemCount, cart/cartTotal).
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Anti-Pattern 4: Mutable State Updates
|
|
156
|
+
|
|
157
|
+
**Also known as:** Direct Mutation, Reference Stomping, Shallow Copy Bypass
|
|
158
|
+
|
|
159
|
+
**Frequency:** High | **Severity:** High | **Detection difficulty:** Medium
|
|
160
|
+
|
|
161
|
+
### What it looks like
|
|
162
|
+
|
|
163
|
+
State is mutated in place instead of creating new references. Pushing to an array
|
|
164
|
+
with `state.items.push(newItem)` instead of `[...state.items, newItem]`. Assigning
|
|
165
|
+
`state.user.name = 'new'` directly. In Flutter, mutating a list and calling
|
|
166
|
+
`setState` without creating a new list instance.
|
|
167
|
+
|
|
168
|
+
### Why developers do it
|
|
169
|
+
|
|
170
|
+
Immutable updates are verbose. Developers from imperative backgrounds (Java, Python,
|
|
171
|
+
C++) think mutation is normal. The code "works" in simple cases because the component
|
|
172
|
+
happens to re-render for other reasons.
|
|
173
|
+
|
|
174
|
+
### What goes wrong
|
|
175
|
+
|
|
176
|
+
- **Silent render skips.** React, Flutter, and SwiftUI use reference equality checks.
|
|
177
|
+
Mutating in place means the reference does not change, so the framework skips the
|
|
178
|
+
re-render and the UI shows stale data.
|
|
179
|
+
- **Time-travel debugging breaks.** Redux DevTools and similar tools depend on
|
|
180
|
+
immutable snapshots. Mutation makes every snapshot point to the same (current) object.
|
|
181
|
+
- **Undo/redo is impossible.** Without distinct state snapshots, there is no previous
|
|
182
|
+
state to restore.
|
|
183
|
+
|
|
184
|
+
### The fix
|
|
185
|
+
|
|
186
|
+
Use immutable update patterns: spread operators, `Object.assign`, or libraries like
|
|
187
|
+
Immer that let you write mutation syntax while producing immutable updates under the
|
|
188
|
+
hood. In Dart/Flutter, use `copyWith` patterns. Enable linting rules that catch direct
|
|
189
|
+
mutations on state objects.
|
|
190
|
+
|
|
191
|
+
### Detection rule
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
ESLint: no-param-reassign on reducer parameters. In Redux Toolkit, Immer is built in.
|
|
195
|
+
Flag .push(), .splice(), .sort() on state arrays. Flag direct property assignment on
|
|
196
|
+
state objects outside of Immer produce() blocks.
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Anti-Pattern 5: Scattered State
|
|
202
|
+
|
|
203
|
+
**Also known as:** State Spaghetti, FrankenState, Orphaned State
|
|
204
|
+
|
|
205
|
+
**Frequency:** High | **Severity:** Medium | **Detection difficulty:** Hard
|
|
206
|
+
|
|
207
|
+
### What it looks like
|
|
208
|
+
|
|
209
|
+
Application state is spread across local component state, multiple context providers,
|
|
210
|
+
a Redux store, URL query parameters, localStorage, cookies, and in-memory caches --
|
|
211
|
+
with no clear ownership boundaries. A developer must check five different locations to
|
|
212
|
+
understand what data a screen is actually displaying.
|
|
213
|
+
|
|
214
|
+
### Why developers do it
|
|
215
|
+
|
|
216
|
+
Organic growth. The first feature used useState. The second needed sharing, so Context
|
|
217
|
+
was added. The third needed persistence, so localStorage was added. The fourth needed
|
|
218
|
+
server sync, so React Query appeared. Nobody drew a boundary diagram.
|
|
219
|
+
|
|
220
|
+
### What goes wrong
|
|
221
|
+
|
|
222
|
+
- **Contradictory state.** The URL says page 2 but the store says page 1. The cache
|
|
223
|
+
says the item exists but localStorage says it was deleted.
|
|
224
|
+
- **Onboarding cost.** New developers cannot predict where to find or update a given
|
|
225
|
+
piece of state without reading the entire codebase.
|
|
226
|
+
- **Refactoring paralysis.** Moving state from one location to another requires
|
|
227
|
+
touching every consumer, so nobody does it.
|
|
228
|
+
|
|
229
|
+
### The fix
|
|
230
|
+
|
|
231
|
+
Categorize state into layers with clear ownership: (1) UI state stays local (open/close,
|
|
232
|
+
hover, focus), (2) Client state goes in a single client store (auth, preferences,
|
|
233
|
+
feature flags), (3) Server state is managed by a server-cache library (React Query,
|
|
234
|
+
SWR, Apollo), (4) URL state lives in the router. Document the boundary and enforce
|
|
235
|
+
it in code review.
|
|
236
|
+
|
|
237
|
+
### Detection rule
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
Audit: count distinct state management mechanisms in use. If >3, review whether
|
|
241
|
+
boundaries are documented. Flag components that read from both a global store and
|
|
242
|
+
local storage for the same entity.
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Anti-Pattern 6: Over-Centralized State
|
|
248
|
+
|
|
249
|
+
**Also known as:** Everything Store, God Store, Redux Maximalism
|
|
250
|
+
|
|
251
|
+
**Frequency:** High | **Severity:** Medium | **Detection difficulty:** Low
|
|
252
|
+
|
|
253
|
+
### What it looks like
|
|
254
|
+
|
|
255
|
+
Every piece of state -- including form input values, modal open/close flags, tooltip
|
|
256
|
+
visibility, and scroll position -- is stored in the global Redux/Vuex/NgRx store.
|
|
257
|
+
The store has hundreds of keys. Every keystroke dispatches an action through middleware.
|
|
258
|
+
|
|
259
|
+
### Why developers do it
|
|
260
|
+
|
|
261
|
+
"Single source of truth" is misunderstood as "single location for all state." Teams
|
|
262
|
+
adopt a rule like "all state in Redux" for consistency without evaluating what belongs
|
|
263
|
+
there. Tutorials demonstrate forms wired to Redux, and teams generalize.
|
|
264
|
+
|
|
265
|
+
### What goes wrong
|
|
266
|
+
|
|
267
|
+
- **Performance death.** Each dispatched action runs through every reducer. Broad
|
|
268
|
+
selectors cause unrelated components to re-render on every keystroke.
|
|
269
|
+
- **Boilerplate explosion.** Action types, action creators, and reducer cases for
|
|
270
|
+
opening a tooltip. Developer velocity drops.
|
|
271
|
+
- **Tight coupling.** Deleting a modal component requires also deleting its slice,
|
|
272
|
+
actions, and selectors from a shared store, making components non-portable.
|
|
273
|
+
|
|
274
|
+
### The fix
|
|
275
|
+
|
|
276
|
+
Reserve global state for data that genuinely crosses component boundaries (auth tokens,
|
|
277
|
+
feature flags, entities shared by multiple routes). Keep ephemeral UI state local to
|
|
278
|
+
the component. Use server-cache libraries for API data instead of manually putting
|
|
279
|
+
fetch results into the store.
|
|
280
|
+
|
|
281
|
+
### Detection rule
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
Flag store keys that are read by exactly one component. Flag actions dispatched only
|
|
285
|
+
from the component that also reduces them. Measure store key count: >50 keys warrants
|
|
286
|
+
a review.
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Anti-Pattern 7: Missing Loading and Error States
|
|
292
|
+
|
|
293
|
+
**Also known as:** Happy Path Only, Optimistic Amnesia, Partial State Modeling
|
|
294
|
+
|
|
295
|
+
**Frequency:** Very High | **Severity:** Medium | **Detection difficulty:** Low
|
|
296
|
+
|
|
297
|
+
### What it looks like
|
|
298
|
+
|
|
299
|
+
An async operation has a state like `{ data: User | null }` with no representation
|
|
300
|
+
for loading or error. Or it has `isLoading` and `isError` as separate booleans that
|
|
301
|
+
can be true simultaneously, creating impossible combinations. The UI shows a blank
|
|
302
|
+
screen or stale data while a request is in flight.
|
|
303
|
+
|
|
304
|
+
### Why developers do it
|
|
305
|
+
|
|
306
|
+
The happy path is the first thing built and demoed. Loading and error states are "I'll
|
|
307
|
+
add those later" items that never get addressed. Separate booleans are the obvious
|
|
308
|
+
(but wrong) modeling choice.
|
|
309
|
+
|
|
310
|
+
### What goes wrong
|
|
311
|
+
|
|
312
|
+
- **Blank screens.** Data is null during loading; the component renders nothing or
|
|
313
|
+
crashes on `.map()` of null.
|
|
314
|
+
- **Phantom spinners.** `isLoading` is set to true but never set to false on error,
|
|
315
|
+
so the spinner runs forever.
|
|
316
|
+
- **Impossible states.** `isLoading: true, isError: true, data: staleData` -- the UI
|
|
317
|
+
cannot decide what to render.
|
|
318
|
+
|
|
319
|
+
### The fix
|
|
320
|
+
|
|
321
|
+
Model async state as a discriminated union / tagged enum: `idle | loading | success
|
|
322
|
+
| error`. Each variant carries only the data relevant to that state. In TypeScript:
|
|
323
|
+
`type State = { status: 'idle' } | { status: 'loading' } | { status: 'success';
|
|
324
|
+
data: T } | { status: 'error'; error: Error }`. Use server-cache libraries that
|
|
325
|
+
model this correctly by default (React Query, SWR).
|
|
326
|
+
|
|
327
|
+
### Detection rule
|
|
328
|
+
|
|
329
|
+
```
|
|
330
|
+
Flag state objects with separate isLoading/isError/isSuccess booleans. Flag async
|
|
331
|
+
functions that set isLoading = true but have no finally/catch that resets it.
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Anti-Pattern 8: Stale Closure over State
|
|
337
|
+
|
|
338
|
+
**Also known as:** Zombie Callback, Captured State, Closure Time Warp
|
|
339
|
+
|
|
340
|
+
**Frequency:** High | **Severity:** High | **Detection difficulty:** Hard
|
|
341
|
+
|
|
342
|
+
### What it looks like
|
|
343
|
+
|
|
344
|
+
A callback or effect captures a state variable at creation time and continues to
|
|
345
|
+
reference that stale value after state updates. Classic example: a `setInterval`
|
|
346
|
+
inside `useEffect` that always logs `count` as 0 because it closed over the initial
|
|
347
|
+
render's value. An event listener registered once that reads state from its creation
|
|
348
|
+
scope.
|
|
349
|
+
|
|
350
|
+
### Why developers do it
|
|
351
|
+
|
|
352
|
+
Closures are a fundamental JavaScript mechanism and are invisible. Developers expect
|
|
353
|
+
the variable to "just have the latest value." The mental model of state as a box that
|
|
354
|
+
updates in place conflicts with React's model of state as a snapshot per render.
|
|
355
|
+
|
|
356
|
+
### What goes wrong
|
|
357
|
+
|
|
358
|
+
- **Silent data loss.** A save handler captures stale form data and overwrites the
|
|
359
|
+
server with old values.
|
|
360
|
+
- **Infinite loops.** A stale dependency causes an effect to re-run when it should not,
|
|
361
|
+
or never run when it should.
|
|
362
|
+
- **Intermittent bugs.** The bug only manifests when state changes between callback
|
|
363
|
+
creation and callback execution -- timing-dependent and hard to reproduce.
|
|
364
|
+
|
|
365
|
+
### The fix
|
|
366
|
+
|
|
367
|
+
Use functional state updates: `setCount(prev => prev + 1)` instead of
|
|
368
|
+
`setCount(count + 1)`. Use refs for values that need to be read by callbacks without
|
|
369
|
+
triggering re-renders (`useRef` + `useEffect` to keep ref in sync). Use the
|
|
370
|
+
`eslint-plugin-react-hooks` exhaustive-deps rule. In newer React, `useEffectEvent`
|
|
371
|
+
(experimental) solves this by design.
|
|
372
|
+
|
|
373
|
+
### Detection rule
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
ESLint react-hooks/exhaustive-deps will flag most cases. Additionally flag
|
|
377
|
+
setInterval/setTimeout callbacks that reference state variables without using
|
|
378
|
+
functional updates.
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Anti-Pattern 9: Async State Race Conditions
|
|
384
|
+
|
|
385
|
+
**Also known as:** Last-Write-Wins, Stale Response, Out-of-Order Updates
|
|
386
|
+
|
|
387
|
+
**Frequency:** High | **Severity:** Critical | **Detection difficulty:** Hard
|
|
388
|
+
|
|
389
|
+
### What it looks like
|
|
390
|
+
|
|
391
|
+
User types in a search box. Requests for "a", "ab", "abc" fire in sequence. The
|
|
392
|
+
response for "a" arrives last (network jitter) and overwrites the correct result for
|
|
393
|
+
"abc." A rapid click on "Save" sends two requests; the second completes first, then
|
|
394
|
+
the first overwrites it. A component unmounts before its fetch completes and the
|
|
395
|
+
callback calls setState on an unmounted component.
|
|
396
|
+
|
|
397
|
+
### Why developers do it
|
|
398
|
+
|
|
399
|
+
Developers write `fetch(url).then(data => setState(data))` without considering that
|
|
400
|
+
multiple calls can be in flight simultaneously. The single-threaded mental model of
|
|
401
|
+
JavaScript obscures the fact that multiple promises can resolve in any order.
|
|
402
|
+
|
|
403
|
+
### What goes wrong
|
|
404
|
+
|
|
405
|
+
- **Wrong data displayed.** The UI shows results for a query the user has already
|
|
406
|
+
moved past.
|
|
407
|
+
- **Data corruption.** An older mutation response overwrites a newer one on the server
|
|
408
|
+
or in the client cache.
|
|
409
|
+
- **Memory leaks and warnings.** Setting state on unmounted components.
|
|
410
|
+
|
|
411
|
+
### The fix
|
|
412
|
+
|
|
413
|
+
Use an abort controller: `const controller = new AbortController()` passed to fetch,
|
|
414
|
+
and abort in the cleanup function. Or use a boolean ignore flag:
|
|
415
|
+
`useEffect(() => { let ignore = false; fetch().then(d => { if (!ignore) setData(d) });
|
|
416
|
+
return () => { ignore = true } })`. Prefer libraries like React Query / SWR / Apollo
|
|
417
|
+
that handle cancellation and deduplication automatically.
|
|
418
|
+
|
|
419
|
+
### Detection rule
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
Flag useEffect with async fetch calls that do not return a cleanup function. Flag
|
|
423
|
+
setState calls inside .then() without a guard. ESLint plugin eslint-plugin-react-hooks
|
|
424
|
+
flags missing cleanup in some cases.
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Anti-Pattern 10: Optimistic UI without Rollback
|
|
430
|
+
|
|
431
|
+
**Also known as:** Fire-and-Forget Optimism, Hope-Driven UI
|
|
432
|
+
|
|
433
|
+
**Frequency:** Medium | **Severity:** High | **Detection difficulty:** Medium
|
|
434
|
+
|
|
435
|
+
### What it looks like
|
|
436
|
+
|
|
437
|
+
The UI immediately reflects a user action (like deleting an item) without waiting for
|
|
438
|
+
the server response, but has no mechanism to revert the change if the server returns
|
|
439
|
+
an error. The item disappears from the list, the server rejects the delete (403), and
|
|
440
|
+
the item is gone from the UI but still exists on the server. A refresh brings it back,
|
|
441
|
+
confusing the user.
|
|
442
|
+
|
|
443
|
+
### Why developers do it
|
|
444
|
+
|
|
445
|
+
Optimistic UI is presented as a UX best practice -- "make the app feel instant." The
|
|
446
|
+
happy path is implemented first; rollback is deferred and forgotten. Rolling back is
|
|
447
|
+
genuinely hard when multiple optimistic mutations are queued.
|
|
448
|
+
|
|
449
|
+
### What goes wrong
|
|
450
|
+
|
|
451
|
+
- **Phantom state.** The UI and server disagree on reality. Users take further actions
|
|
452
|
+
based on the phantom state, compounding the inconsistency.
|
|
453
|
+
- **Lost edits.** Optimistic create succeeds in UI, server rejects, user adds data to
|
|
454
|
+
the phantom entity, all of which is lost on refresh.
|
|
455
|
+
- **Cascading failures.** Multiple optimistic updates build on each other; rolling back
|
|
456
|
+
one requires rolling back all dependents.
|
|
457
|
+
|
|
458
|
+
### The fix
|
|
459
|
+
|
|
460
|
+
Always pair optimistic updates with a rollback path. Cache the previous state before
|
|
461
|
+
applying the optimistic change. On server error, restore the cached state and show a
|
|
462
|
+
user-facing error. Use libraries with built-in optimistic mutation support (React
|
|
463
|
+
Query's `onMutate` / `onError` / `onSettled`, Apollo's `optimisticResponse`). Avoid
|
|
464
|
+
optimistic UI for operations with high failure rates or irreversible consequences.
|
|
465
|
+
|
|
466
|
+
### Detection rule
|
|
467
|
+
|
|
468
|
+
```
|
|
469
|
+
Flag mutation functions that call setState before awaiting the server response but
|
|
470
|
+
have no catch/error handler that restores previous state. Review optimistic update
|
|
471
|
+
implementations for the presence of rollback logic.
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## Anti-Pattern 11: State Management Library Overkill
|
|
477
|
+
|
|
478
|
+
**Also known as:** Redux for a Counter, Ceremony-Driven Development, Premature Architecture
|
|
479
|
+
|
|
480
|
+
**Frequency:** High | **Severity:** Low | **Detection difficulty:** Low
|
|
481
|
+
|
|
482
|
+
### What it looks like
|
|
483
|
+
|
|
484
|
+
A small application with 3-5 pieces of shared state uses Redux (or NgRx, or Vuex)
|
|
485
|
+
with full action/reducer/middleware/selector ceremony. A simple form has action types
|
|
486
|
+
like `SET_FIRST_NAME`, `SET_LAST_NAME`, `SET_EMAIL`. The boilerplate exceeds the
|
|
487
|
+
business logic by 5:1.
|
|
488
|
+
|
|
489
|
+
### Why developers do it
|
|
490
|
+
|
|
491
|
+
"We might scale later." Blog posts and conference talks showcase Redux for large apps,
|
|
492
|
+
and teams cargo-cult the same setup for a to-do app. Hiring managers list Redux as a
|
|
493
|
+
requirement, so developers use it everywhere to stay practiced.
|
|
494
|
+
|
|
495
|
+
### What goes wrong
|
|
496
|
+
|
|
497
|
+
- **Velocity death.** Adding a feature requires creating a slice, actions, selectors,
|
|
498
|
+
and possibly sagas -- for something `useState` handles in one line.
|
|
499
|
+
- **Cognitive overhead.** New developers are overwhelmed by indirection before they
|
|
500
|
+
even see the business logic.
|
|
501
|
+
- **Abandoned migration.** The team later wants to remove Redux but it is wired into
|
|
502
|
+
everything, so it stays as legacy weight.
|
|
503
|
+
|
|
504
|
+
### The fix
|
|
505
|
+
|
|
506
|
+
Start with the simplest state mechanism (`useState`, `useReducer`, Context). Add a
|
|
507
|
+
state library only when you observe a concrete problem: prop drilling through 4+
|
|
508
|
+
levels, frequent cross-component state sharing, or need for time-travel debugging.
|
|
509
|
+
For server data, use a server-cache library instead of manually putting API data into
|
|
510
|
+
Redux.
|
|
511
|
+
|
|
512
|
+
### Detection rule
|
|
513
|
+
|
|
514
|
+
```
|
|
515
|
+
Count actions and reducers vs. components. If the ratio of actions to components
|
|
516
|
+
exceeds 3:1 and the app has fewer than 20 routes, evaluate whether the library is
|
|
517
|
+
justified.
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
## Anti-Pattern 12: Implicit State Machines (Boolean Soup)
|
|
523
|
+
|
|
524
|
+
**Also known as:** Boolean Flag Hell, Impossible States, Ad-Hoc State Modeling
|
|
525
|
+
|
|
526
|
+
**Frequency:** Very High | **Severity:** High | **Detection difficulty:** Low
|
|
527
|
+
|
|
528
|
+
### What it looks like
|
|
529
|
+
|
|
530
|
+
A component manages its state with multiple independent booleans: `isLoading`,
|
|
531
|
+
`isError`, `isSuccess`, `isRetrying`, `isOpen`, `isAnimating`. With 6 booleans
|
|
532
|
+
there are 64 possible combinations, but only 5-8 are valid. Nothing in the code
|
|
533
|
+
prevents the other 56+ impossible combinations.
|
|
534
|
+
|
|
535
|
+
### Why developers do it
|
|
536
|
+
|
|
537
|
+
Each boolean is added incrementally as new requirements emerge. "We need a loading
|
|
538
|
+
state" -- add a boolean. "Now we need error handling" -- add another boolean. The
|
|
539
|
+
cognitive load of each addition is small, but the combinatorial explosion is not
|
|
540
|
+
visible until bugs appear.
|
|
541
|
+
|
|
542
|
+
### What goes wrong
|
|
543
|
+
|
|
544
|
+
- **Impossible states.** `isLoading: true` AND `isError: true` simultaneously. The UI
|
|
545
|
+
shows a spinner overlaid on an error message.
|
|
546
|
+
- **Missing transitions.** The code forgets to set `isLoading = false` in the error
|
|
547
|
+
path. The spinner runs forever.
|
|
548
|
+
- **Untestable.** Testing 64 combinations is impractical, so edge cases go uncovered.
|
|
549
|
+
Kent C. Dodds: "Stop using isLoading booleans."
|
|
550
|
+
|
|
551
|
+
### The fix
|
|
552
|
+
|
|
553
|
+
Replace boolean flags with a single status field modeled as a discriminated union or
|
|
554
|
+
enum: `type Status = 'idle' | 'loading' | 'success' | 'error'`. For complex flows,
|
|
555
|
+
use a state machine library (XState, Robot) that makes transitions explicit and
|
|
556
|
+
impossible states unreachable by construction.
|
|
557
|
+
|
|
558
|
+
### Detection rule
|
|
559
|
+
|
|
560
|
+
```
|
|
561
|
+
Flag components with 3+ boolean state variables whose names start with is/has/should.
|
|
562
|
+
Flag setState calls that set multiple booleans simultaneously. Static analysis: count
|
|
563
|
+
boolean state variables per component.
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## Anti-Pattern 13: Zombie Child Problem
|
|
569
|
+
|
|
570
|
+
**Also known as:** Stale Props, Ghost Subscriber, Undead Component
|
|
571
|
+
|
|
572
|
+
**Frequency:** Medium | **Severity:** High | **Detection difficulty:** Hard
|
|
573
|
+
|
|
574
|
+
### What it looks like
|
|
575
|
+
|
|
576
|
+
A parent component renders a list of children connected to a global store. An action
|
|
577
|
+
deletes an entity from the store. The child component's subscription fires before the
|
|
578
|
+
parent re-renders and removes the child. The child tries to read the deleted entity
|
|
579
|
+
from the store, gets `undefined`, and throws. This was a well-documented issue in
|
|
580
|
+
React-Redux v5-v7 and remains possible with hooks-based store subscriptions.
|
|
581
|
+
|
|
582
|
+
### Why developers do it
|
|
583
|
+
|
|
584
|
+
External store subscriptions (Zustand, Redux with `useSelector`, custom pub-sub) do
|
|
585
|
+
not follow React's top-down rendering order. Child subscriptions can fire before
|
|
586
|
+
parent subscriptions, creating a window where the child exists but its data does not.
|
|
587
|
+
|
|
588
|
+
### What goes wrong
|
|
589
|
+
|
|
590
|
+
- **Runtime crashes.** `Cannot read property 'name' of undefined` on a component that
|
|
591
|
+
is about to unmount but has not yet.
|
|
592
|
+
- **Inconsistent UI.** For a brief moment (often a single frame), the child renders
|
|
593
|
+
with missing or default data before being unmounted.
|
|
594
|
+
- **Hard to reproduce.** Depends on subscription ordering and timing; may not appear
|
|
595
|
+
in development but crashes in production under load.
|
|
596
|
+
|
|
597
|
+
### The fix
|
|
598
|
+
|
|
599
|
+
Defensive selectors: always guard against missing data (`const item = useSelector(s =>
|
|
600
|
+
s.items[id] ?? EMPTY_ITEM)`). React-Redux's `useSelector` catches errors thrown during
|
|
601
|
+
store-update-triggered selector execution and forces a re-render. When building custom
|
|
602
|
+
subscription hooks, implement stale-check guards. Libraries like Zustand handle this
|
|
603
|
+
via `useSyncExternalStore`.
|
|
604
|
+
|
|
605
|
+
### Detection rule
|
|
606
|
+
|
|
607
|
+
```
|
|
608
|
+
Flag selectors that access store properties via dynamic IDs without null/undefined
|
|
609
|
+
guards. Flag connected child components in lists that do not handle the case where
|
|
610
|
+
their entity has been deleted.
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
## Anti-Pattern 14: Props as Initial State
|
|
616
|
+
|
|
617
|
+
**Also known as:** Snapshot State, Stale Initialization, One-Time Copy
|
|
618
|
+
|
|
619
|
+
**Frequency:** High | **Severity:** Medium | **Detection difficulty:** Low
|
|
620
|
+
|
|
621
|
+
### What it looks like
|
|
622
|
+
|
|
623
|
+
A child component receives a prop and copies it into local state in the constructor
|
|
624
|
+
or `useState` initializer: `const [name, setName] = useState(props.name)`. When the
|
|
625
|
+
parent updates `props.name`, the child continues to display the old value because
|
|
626
|
+
`useState` only uses the initial value on mount.
|
|
627
|
+
|
|
628
|
+
### Why developers do it
|
|
629
|
+
|
|
630
|
+
The component needs to allow local edits (e.g., a form pre-filled from props). The
|
|
631
|
+
developer reaches for `useState` because they know the child needs its own mutable
|
|
632
|
+
copy. In class components, `getInitialState` from props was a common tutorial pattern.
|
|
633
|
+
|
|
634
|
+
### What goes wrong
|
|
635
|
+
|
|
636
|
+
- **Stale display.** Parent updates the entity; child shows outdated data until
|
|
637
|
+
remounted.
|
|
638
|
+
- **Data loss.** User edits the child form, parent re-renders (but does not remount
|
|
639
|
+
the child), user's edits are preserved but the prop change is lost. Or worse, a key
|
|
640
|
+
change remounts the child and wipes user edits.
|
|
641
|
+
- **Source of truth confusion.** Is the parent or the child authoritative? Neither
|
|
642
|
+
developer nor user can tell.
|
|
643
|
+
|
|
644
|
+
### The fix
|
|
645
|
+
|
|
646
|
+
If the child should always reflect the parent's data, do not use state -- use the prop
|
|
647
|
+
directly. If the child needs an editable copy, use a `key` prop tied to the entity ID
|
|
648
|
+
so React remounts on entity change: `<Editor key={user.id} user={user} />`. Name
|
|
649
|
+
initial-value props clearly: `initialValue` or `defaultValue` to signal intent. Use
|
|
650
|
+
controlled components where the parent owns both the value and the change handler.
|
|
651
|
+
|
|
652
|
+
### Detection rule
|
|
653
|
+
|
|
654
|
+
```
|
|
655
|
+
Flag useState calls where the initializer is a prop that is not named initial* or
|
|
656
|
+
default*. Flag components that both receive a prop and have state of the same name.
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
---
|
|
660
|
+
|
|
661
|
+
## Anti-Pattern 15: Two-Way Data Binding Chaos
|
|
662
|
+
|
|
663
|
+
**Also known as:** Bidirectional Mutation, Digest Cycle Storms, Ping-Pong State
|
|
664
|
+
|
|
665
|
+
**Frequency:** Medium | **Severity:** High | **Detection difficulty:** Medium
|
|
666
|
+
|
|
667
|
+
### What it looks like
|
|
668
|
+
|
|
669
|
+
Two or more components are bound bidirectionally to the same data source. Changing
|
|
670
|
+
component A updates the model, which triggers component B to update, which triggers
|
|
671
|
+
a watcher that updates the model again. In AngularJS, this manifested as infinite
|
|
672
|
+
digest cycles. In modern frameworks, it appears as circular `useEffect` chains or
|
|
673
|
+
Vue watchers that trigger each other.
|
|
674
|
+
|
|
675
|
+
### Why developers do it
|
|
676
|
+
|
|
677
|
+
Two-way binding is convenient for forms and feels natural: "the input and the model
|
|
678
|
+
should always agree." AngularJS made it the default. Vue's `v-model` encourages it.
|
|
679
|
+
Developers extend the pattern beyond simple inputs to complex cross-component
|
|
680
|
+
synchronization.
|
|
681
|
+
|
|
682
|
+
### What goes wrong
|
|
683
|
+
|
|
684
|
+
- **Infinite loops.** A changes B, B changes A, repeat. AngularJS had a hard limit of
|
|
685
|
+
10 digest cycles before throwing. Modern frameworks may produce infinite re-render
|
|
686
|
+
loops that freeze the browser.
|
|
687
|
+
- **Performance degradation.** Every binding creates watchers. Hundreds of bindings
|
|
688
|
+
cause page lag on every keystroke.
|
|
689
|
+
- **Unpredictable data flow.** With bidirectional flows, data can arrive at a component
|
|
690
|
+
from multiple directions, making debugging which source caused a change very
|
|
691
|
+
difficult.
|
|
692
|
+
|
|
693
|
+
### The fix
|
|
694
|
+
|
|
695
|
+
Prefer unidirectional data flow: parent passes data down, child emits events up. Use
|
|
696
|
+
two-way binding only for leaf-level form inputs. For complex forms, use a form library
|
|
697
|
+
that manages the binding lifecycle (React Hook Form, Formik, Angular Reactive Forms).
|
|
698
|
+
Never create circular watcher/effect dependencies.
|
|
699
|
+
|
|
700
|
+
### Detection rule
|
|
701
|
+
|
|
702
|
+
```
|
|
703
|
+
Flag circular dependencies between useEffect hooks (effect A sets state that is a
|
|
704
|
+
dependency of effect B, and vice versa). Flag Vue watchers that modify the same data
|
|
705
|
+
they observe. In AngularJS, flag $watch callbacks that trigger $apply/$digest.
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
---
|
|
709
|
+
|
|
710
|
+
## Anti-Pattern 16: State Stored in DOM
|
|
711
|
+
|
|
712
|
+
**Also known as:** DOM as Database, jQuery State Management, Data Attribute Abuse
|
|
713
|
+
|
|
714
|
+
**Frequency:** Medium (declining) | **Severity:** Medium | **Detection difficulty:** Low
|
|
715
|
+
|
|
716
|
+
### What it looks like
|
|
717
|
+
|
|
718
|
+
Application state is stored in DOM elements: data attributes, hidden input fields,
|
|
719
|
+
CSS class names toggled to represent state, or jQuery's `$.data()` cache. A "selected"
|
|
720
|
+
state is tracked by the presence of a `.selected` CSS class rather than a JavaScript
|
|
721
|
+
variable. Form data is read from the DOM instead of maintained in a model.
|
|
722
|
+
|
|
723
|
+
### Why developers do it
|
|
724
|
+
|
|
725
|
+
This was the dominant pattern in the jQuery era. Developers new to frameworks sometimes
|
|
726
|
+
carry the habit forward. It is the path of least resistance when there is no state
|
|
727
|
+
management layer. Server-rendered pages with progressive enhancement sometimes start
|
|
728
|
+
this way.
|
|
729
|
+
|
|
730
|
+
### What goes wrong
|
|
731
|
+
|
|
732
|
+
- **Performance.** Reading state from the DOM requires DOM queries, which are orders of
|
|
733
|
+
magnitude slower than reading a JavaScript variable.
|
|
734
|
+
- **Fragility.** A CSS refactor removes the `.selected` class, and the state mechanism
|
|
735
|
+
silently breaks. DOM structure changes break querySelector-based state lookups.
|
|
736
|
+
- **No reactivity.** DOM mutations do not trigger framework-level re-renders. The UI
|
|
737
|
+
and the "state" can diverge without any error.
|
|
738
|
+
- **Testability.** Testing requires a full DOM environment (JSDOM or a browser), making
|
|
739
|
+
unit tests slow and brittle.
|
|
740
|
+
|
|
741
|
+
### The fix
|
|
742
|
+
|
|
743
|
+
Separate state from presentation. Store state in JavaScript variables, and let the
|
|
744
|
+
framework render the DOM from that state. If progressive enhancement is needed, read
|
|
745
|
+
initial state from DOM/data attributes once at startup and then manage it in JavaScript.
|
|
746
|
+
|
|
747
|
+
### Detection rule
|
|
748
|
+
|
|
749
|
+
```
|
|
750
|
+
Flag jQuery $.data() usage in codebases using React/Vue/Angular. Flag
|
|
751
|
+
document.querySelector used for reading state. Flag CSS class checks used in
|
|
752
|
+
conditional logic (classList.contains in business logic).
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
---
|
|
756
|
+
|
|
757
|
+
## Anti-Pattern 17: Storing Computed Values in State
|
|
758
|
+
|
|
759
|
+
**Also known as:** Redundant State, Memoization Confusion, Cache-in-State
|
|
760
|
+
|
|
761
|
+
**Frequency:** High | **Severity:** Low | **Detection difficulty:** Low
|
|
762
|
+
|
|
763
|
+
### What it looks like
|
|
764
|
+
|
|
765
|
+
State contains values that are purely derived from other state: `fullName` stored
|
|
766
|
+
alongside `firstName` and `lastName`. `filteredItems` stored alongside `items` and
|
|
767
|
+
`filterQuery`. `isValid` stored alongside the form fields it validates. These values
|
|
768
|
+
are updated via effects or event handlers that "sync" them with their sources.
|
|
769
|
+
|
|
770
|
+
### Why developers do it
|
|
771
|
+
|
|
772
|
+
It feels like caching. Developers worry about re-computing on every render. In class
|
|
773
|
+
components, there was no `useMemo` equivalent, so storing computed values was the norm.
|
|
774
|
+
Some developers confuse memoization with state management.
|
|
775
|
+
|
|
776
|
+
### What goes wrong
|
|
777
|
+
|
|
778
|
+
- **Sync bugs.** The computed value is updated in one code path but not another.
|
|
779
|
+
`fullName` reflects a name change from the form but not from a server push.
|
|
780
|
+
- **Extra renders.** Setting state inside `useEffect` to "sync" triggers a second
|
|
781
|
+
render cycle per update.
|
|
782
|
+
- **Increased state surface.** More state means more reducer cases, more tests, and
|
|
783
|
+
more potential for inconsistency.
|
|
784
|
+
|
|
785
|
+
### The fix
|
|
786
|
+
|
|
787
|
+
Compute values inline: `const fullName = \`\${firstName} \${lastName}\``. For expensive
|
|
788
|
+
computations, use `useMemo` (React), `computed` (Vue/MobX), or `createSelector`
|
|
789
|
+
(Redux). These are memoization, not state, and they have no sync bugs because they
|
|
790
|
+
derive from the source directly.
|
|
791
|
+
|
|
792
|
+
### Detection rule
|
|
793
|
+
|
|
794
|
+
```
|
|
795
|
+
Flag state variables whose setter is only ever called inside useEffect with
|
|
796
|
+
dependencies that are other state variables. Flag state variables that are never set
|
|
797
|
+
by user actions, only by other state changes.
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
---
|
|
801
|
+
|
|
802
|
+
## Anti-Pattern 18: Not Normalizing Relational Data
|
|
803
|
+
|
|
804
|
+
**Also known as:** Nested State Tree, Deep Copy Hell, Entity Spaghetti
|
|
805
|
+
|
|
806
|
+
**Frequency:** High | **Severity:** Medium | **Detection difficulty:** Medium
|
|
807
|
+
|
|
808
|
+
### What it looks like
|
|
809
|
+
|
|
810
|
+
API responses are stored in state exactly as received: deeply nested objects with
|
|
811
|
+
embedded related entities. A `Post` object contains an array of `Comment` objects,
|
|
812
|
+
each containing a `User` object. Updating a user's avatar requires finding every post,
|
|
813
|
+
then every comment by that user, and updating each nested copy.
|
|
814
|
+
|
|
815
|
+
### Why developers do it
|
|
816
|
+
|
|
817
|
+
The API response shape is convenient -- it matches the UI hierarchy. Normalizing feels
|
|
818
|
+
like premature optimization. The initial code is simpler: just `setState(apiResponse)`.
|
|
819
|
+
|
|
820
|
+
### What goes wrong
|
|
821
|
+
|
|
822
|
+
- **Update complexity.** Changing one entity requires deep traversal and immutable
|
|
823
|
+
updates at every nesting level. Reducers become deeply nested spread operations.
|
|
824
|
+
- **Inconsistency.** The same user appears in 15 comments with 15 copies of their
|
|
825
|
+
avatar URL. Updating one and missing the others creates visual inconsistency.
|
|
826
|
+
- **Performance.** Deeply nested equality checks are expensive. Any change to any
|
|
827
|
+
comment causes the entire post tree to be treated as changed.
|
|
828
|
+
|
|
829
|
+
### The fix
|
|
830
|
+
|
|
831
|
+
Normalize data into flat lookup tables: `{ users: { byId: {}, allIds: [] }, posts:
|
|
832
|
+
{ byId: {}, allIds: [] }, comments: { byId: {}, allIds: [] } }`. Use libraries like
|
|
833
|
+
`normalizr` to automate the transformation from API responses. Reference related
|
|
834
|
+
entities by ID. Use selectors to denormalize for display.
|
|
835
|
+
|
|
836
|
+
### Detection rule
|
|
837
|
+
|
|
838
|
+
```
|
|
839
|
+
Flag state objects nested more than 3 levels deep. Flag reducer spread operations
|
|
840
|
+
with more than 2 levels of nesting. Flag API responses stored directly in state
|
|
841
|
+
without transformation.
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
---
|
|
845
|
+
|
|
846
|
+
## Anti-Pattern 19: Exposing Internal State Implementation
|
|
847
|
+
|
|
848
|
+
**Also known as:** Leaky State Abstraction, Public Internals, State Coupling
|
|
849
|
+
|
|
850
|
+
**Frequency:** Medium | **Severity:** Medium | **Detection difficulty:** Medium
|
|
851
|
+
|
|
852
|
+
### What it looks like
|
|
853
|
+
|
|
854
|
+
A component or module exposes its internal state structure to consumers. A store's
|
|
855
|
+
shape becomes a public API contract: consumers directly access `store.state.ui.
|
|
856
|
+
modals.confirmDialog.isOpen` instead of calling `store.isConfirmDialogOpen()`. A class
|
|
857
|
+
exposes a mutable collection via a getter, allowing external code to modify internal
|
|
858
|
+
state without going through methods.
|
|
859
|
+
|
|
860
|
+
### Why developers do it
|
|
861
|
+
|
|
862
|
+
Direct property access is faster to write than defining a selector or method.
|
|
863
|
+
Encapsulation feels like over-engineering for "simple" state. In Redux, the state
|
|
864
|
+
shape is inherently visible to all selectors, making it easy to couple deeply.
|
|
865
|
+
|
|
866
|
+
### What goes wrong
|
|
867
|
+
|
|
868
|
+
- **Refactoring paralysis.** Renaming a state key or restructuring the state tree
|
|
869
|
+
breaks every consumer that accessed it directly. What should be an internal change
|
|
870
|
+
becomes a cross-codebase migration.
|
|
871
|
+
- **Invariant violations.** External code modifies internal state without going through
|
|
872
|
+
validation logic, putting the module into an inconsistent state.
|
|
873
|
+
- **Brittle tests.** Tests assert against internal state shape instead of observable
|
|
874
|
+
behavior, breaking on every internal refactor.
|
|
875
|
+
|
|
876
|
+
### The fix
|
|
877
|
+
|
|
878
|
+
Expose state through selectors, computed properties, or accessor methods. In Redux,
|
|
879
|
+
co-locate selectors with the slice and export only selectors, not the state shape.
|
|
880
|
+
In OOP, return defensive copies of collections, not references. In classes, make state
|
|
881
|
+
fields private and expose behavior through methods.
|
|
882
|
+
|
|
883
|
+
### Detection rule
|
|
884
|
+
|
|
885
|
+
```
|
|
886
|
+
Flag external files importing and accessing store state paths deeper than 2 levels.
|
|
887
|
+
Flag public mutable fields on state-holding classes. Flag tests that assert against
|
|
888
|
+
internal state structure rather than observable behavior.
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
## Anti-Pattern 20: Cache as Source of Truth
|
|
894
|
+
|
|
895
|
+
**Also known as:** Cache Statization, Client Mirror, Optimistic Cache
|
|
896
|
+
|
|
897
|
+
**Frequency:** Medium | **Severity:** High | **Detection difficulty:** Medium
|
|
898
|
+
|
|
899
|
+
### What it looks like
|
|
900
|
+
|
|
901
|
+
The application treats its client-side cache (Redux store, Apollo cache, React Query
|
|
902
|
+
cache) as the authoritative source of data rather than the server. Components read
|
|
903
|
+
from the cache and never re-fetch, even after mutations. Cache entries are manually
|
|
904
|
+
updated to avoid re-fetching, creating a parallel "database" in the browser. The
|
|
905
|
+
server and client gradually diverge.
|
|
906
|
+
|
|
907
|
+
### Why developers do it
|
|
908
|
+
|
|
909
|
+
Re-fetching feels wasteful. Cache updates are faster than round-trips. The app "works"
|
|
910
|
+
in demo environments where the user is the only client. Developers conflate "cache for
|
|
911
|
+
performance" with "cache as state."
|
|
912
|
+
|
|
913
|
+
### What goes wrong
|
|
914
|
+
|
|
915
|
+
- **Stale data.** Another user (or another tab) modifies the data on the server. The
|
|
916
|
+
client never learns about it because it trusts its cache.
|
|
917
|
+
- **Cache invalidation bugs.** Manually updating cache entries after mutations is
|
|
918
|
+
error-prone. Miss one cache key and the UI shows contradictory data.
|
|
919
|
+
- **Offline divergence.** The cache and server diverge during offline periods. Merging
|
|
920
|
+
them back is a distributed systems problem that most apps are not equipped to solve.
|
|
921
|
+
- **FrankenState.** Half the data is managed as "state" in Redux, half as "cache" in
|
|
922
|
+
React Query. Boundaries blur and data is duplicated across both.
|
|
923
|
+
|
|
924
|
+
### The fix
|
|
925
|
+
|
|
926
|
+
Treat the server as the source of truth and the client as a cache. Use server-cache
|
|
927
|
+
libraries (React Query, SWR, Apollo) with proper cache invalidation strategies:
|
|
928
|
+
refetch on mount, refetch on window focus, invalidate after mutations. Set appropriate
|
|
929
|
+
stale times. For truly offline-first apps, use dedicated offline sync solutions (e.g.,
|
|
930
|
+
WatermelonDB, PouchDB) rather than ad-hoc cache management.
|
|
931
|
+
|
|
932
|
+
### Detection rule
|
|
933
|
+
|
|
934
|
+
```
|
|
935
|
+
Flag manual cache.writeQuery / queryClient.setQueryData calls that are not
|
|
936
|
+
accompanied by an invalidation or refetch. Flag applications where server data is
|
|
937
|
+
only fetched once (on mount) and never refreshed. Flag store data that is >5 minutes
|
|
938
|
+
old with no TTL mechanism.
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
---
|
|
942
|
+
|
|
943
|
+
## Root Cause Analysis
|
|
944
|
+
|
|
945
|
+
Most state management anti-patterns trace back to five root causes:
|
|
946
|
+
|
|
947
|
+
### 1. No Ownership Model
|
|
948
|
+
|
|
949
|
+
When nobody defines which layer owns each piece of state, state proliferates across
|
|
950
|
+
layers and duplicates. Every team or feature creates its own state silo. The fix is
|
|
951
|
+
to create a state ownership diagram early and enforce it in code review.
|
|
952
|
+
|
|
953
|
+
### 2. Imperative Mental Model
|
|
954
|
+
|
|
955
|
+
Developers with imperative backgrounds think of state as mutable boxes. Frameworks
|
|
956
|
+
like React, Flutter, and SwiftUI are declarative and use value semantics for change
|
|
957
|
+
detection. The clash produces mutation bugs, stale closures, and missed re-renders.
|
|
958
|
+
|
|
959
|
+
### 3. Premature Abstraction
|
|
960
|
+
|
|
961
|
+
Adopting a state management library (Redux, MobX, Vuex) before understanding the
|
|
962
|
+
problem leads to over-engineering. The library's patterns become the architecture
|
|
963
|
+
instead of serving it. Start simple, add complexity when concrete problems arise.
|
|
964
|
+
|
|
965
|
+
### 4. Ignoring Async Complexity
|
|
966
|
+
|
|
967
|
+
Synchronous state management is straightforward. Most bugs live at the boundary
|
|
968
|
+
between sync and async: race conditions, stale closures, optimistic updates, and
|
|
969
|
+
cache invalidation. Developers underestimate this complexity because the happy path
|
|
970
|
+
works in development.
|
|
971
|
+
|
|
972
|
+
### 5. Missing State Modeling
|
|
973
|
+
|
|
974
|
+
State is treated as a bag of variables instead of a model with defined states and
|
|
975
|
+
transitions. Boolean flags replace finite state machines. Loading/error/success is
|
|
976
|
+
an afterthought. The fix is to model state as a type system with discriminated unions
|
|
977
|
+
or state machines before writing any UI code.
|
|
978
|
+
|
|
979
|
+
---
|
|
980
|
+
|
|
981
|
+
## Self-Check Questions
|
|
982
|
+
|
|
983
|
+
Use these questions during code review or architecture discussions:
|
|
984
|
+
|
|
985
|
+
1. **Can I point to exactly one location in the codebase that owns this data?**
|
|
986
|
+
If you find 2+ locations, you have state duplication (Anti-Pattern 2).
|
|
987
|
+
|
|
988
|
+
2. **If I delete this state variable, can I recompute it from other state?**
|
|
989
|
+
If yes, it is derived state and should not be stored (Anti-Patterns 3, 17).
|
|
990
|
+
|
|
991
|
+
3. **What happens if two async operations complete out of order?**
|
|
992
|
+
If the answer is "the wrong data displays," you have a race condition
|
|
993
|
+
(Anti-Pattern 9).
|
|
994
|
+
|
|
995
|
+
4. **Can this component be in a state that should be impossible?**
|
|
996
|
+
List all boolean state variables. If 2^N exceeds your valid states by more than
|
|
997
|
+
2x, you need a state machine (Anti-Pattern 12).
|
|
998
|
+
|
|
999
|
+
5. **What happens when the server says no to our optimistic update?**
|
|
1000
|
+
If there is no rollback path, you have Anti-Pattern 10.
|
|
1001
|
+
|
|
1002
|
+
6. **Is this state management library earning its keep?**
|
|
1003
|
+
If more than 60% of actions are consumed by a single component, the library is
|
|
1004
|
+
overkill (Anti-Pattern 11).
|
|
1005
|
+
|
|
1006
|
+
7. **How many state management mechanisms are in this app?**
|
|
1007
|
+
If the answer is >3 (e.g., Redux + Context + localStorage + URL params + React
|
|
1008
|
+
Query), review boundaries (Anti-Pattern 5).
|
|
1009
|
+
|
|
1010
|
+
8. **Would a new developer know where to find the state for this feature?**
|
|
1011
|
+
If it takes more than 30 seconds to locate, state is too scattered (Anti-Pattern 5)
|
|
1012
|
+
or too coupled to internals (Anti-Pattern 19).
|
|
1013
|
+
|
|
1014
|
+
9. **If I refactor the internal state shape, how many files break?**
|
|
1015
|
+
If more than the module itself, you are exposing internal state (Anti-Pattern 19).
|
|
1016
|
+
|
|
1017
|
+
10. **When was this cached data last validated against the server?**
|
|
1018
|
+
If the answer is "only on initial load," you may be treating cache as source of
|
|
1019
|
+
truth (Anti-Pattern 20).
|
|
1020
|
+
|
|
1021
|
+
---
|
|
1022
|
+
|
|
1023
|
+
## Code Smell Quick Reference
|
|
1024
|
+
|
|
1025
|
+
| Smell | Anti-Pattern | Severity |
|
|
1026
|
+
|---|---|---|
|
|
1027
|
+
| Module-scope `let` imported by multiple files | #1 Global Mutable Singleton | High |
|
|
1028
|
+
| Same entity type in 2+ store slices | #2 State Duplication | High |
|
|
1029
|
+
| `useEffect` that only calls `setState` | #3 Derived State Stored | Medium |
|
|
1030
|
+
| `.push()` / `.splice()` on state array | #4 Mutable State Updates | High |
|
|
1031
|
+
| 3+ state mechanisms (Redux + Context + localStorage) | #5 Scattered State | Medium |
|
|
1032
|
+
| Store key read by exactly 1 component | #6 Over-Centralized State | Medium |
|
|
1033
|
+
| `data: null` with no `loading`/`error` status | #7 Missing Loading/Error | Medium |
|
|
1034
|
+
| `setInterval` referencing state without functional update | #8 Stale Closure | High |
|
|
1035
|
+
| `.then(setData)` without abort/ignore guard | #9 Async Race Condition | Critical |
|
|
1036
|
+
| Optimistic setState with no catch/rollback | #10 Optimistic without Rollback | High |
|
|
1037
|
+
| Action-to-component ratio > 3:1 in small app | #11 Library Overkill | Low |
|
|
1038
|
+
| 3+ boolean `is*` state variables in one component | #12 Boolean Soup | High |
|
|
1039
|
+
| Selector accessing `store[id]` without null guard | #13 Zombie Child | High |
|
|
1040
|
+
| `useState(props.x)` where prop is not named `initial*` | #14 Props as Initial State | Medium |
|
|
1041
|
+
| Circular `useEffect` / watcher dependencies | #15 Two-Way Binding Chaos | High |
|
|
1042
|
+
| `document.querySelector` in React conditional logic | #16 State in DOM | Medium |
|
|
1043
|
+
| State variable only set inside `useEffect` | #17 Computed Values in State | Low |
|
|
1044
|
+
| State objects nested 3+ levels deep | #18 Non-Normalized Data | Medium |
|
|
1045
|
+
| External file accessing `store.state.a.b.c.d` | #19 Exposed Internal State | Medium |
|
|
1046
|
+
| Server data fetched once, never refreshed | #20 Cache as Source of Truth | High |
|
|
1047
|
+
|
|
1048
|
+
---
|
|
1049
|
+
|
|
1050
|
+
## Sources
|
|
1051
|
+
|
|
1052
|
+
- [Redux Anti-Patterns - State Management (Minko Gechev)](https://blog.mgechev.com/2017/12/07/redux-anti-patterns-race-conditions-state-management-duplication/)
|
|
1053
|
+
- [State Management Anti Patterns (Source Allies)](https://www.sourceallies.com/2020/11/state-management-anti-patterns/)
|
|
1054
|
+
- [Global Mutable State (Eric Normand)](https://ericnormand.me/article/global-mutable-state)
|
|
1055
|
+
- [Impact of Mutable Global State on Defect Proneness (IEEE Xplore)](https://ieeexplore.ieee.org/document/9118816)
|
|
1056
|
+
- [Stop Using isLoading Booleans (Kent C. Dodds)](https://kentcdodds.com/blog/stop-using-isloading-booleans)
|
|
1057
|
+
- [Make Impossible States Impossible (Kent C. Dodds)](https://kentcdodds.com/blog/make-impossible-states-impossible)
|
|
1058
|
+
- [Don't Sync State. Derive It! (Kent C. Dodds)](https://kentcdodds.com/blog/dont-sync-state-derive-it)
|
|
1059
|
+
- [You Probably Don't Need Derived State (React Blog)](https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html)
|
|
1060
|
+
- [Be Aware of Stale Closures when Using React Hooks (Dmitri Pavlutin)](https://dmitripavlutin.com/react-hooks-stale-closures/)
|
|
1061
|
+
- [5 Mistakes to Avoid When Using React Hooks (Dmitri Pavlutin)](https://dmitripavlutin.com/react-hooks-mistakes-to-avoid/)
|
|
1062
|
+
- [Stale Props and Zombie Children in Redux (Kai Hao)](https://kaihao.dev/posts/Stale-props-and-zombie-children-in-Redux)
|
|
1063
|
+
- [React-Redux Roadmap: Context, Subscriptions, and Hooks (GitHub Issue #1177)](https://github.com/reduxjs/react-redux/issues/1177)
|
|
1064
|
+
- [Normalizing State Shape (Redux Docs)](https://redux.js.org/usage/structuring-reducers/normalizing-state-shape)
|
|
1065
|
+
- [Props in Initial State is an Anti-pattern (React Patterns)](https://reactpatterns.js.org/docs/props-in-initial-state-is-an-anti-pattern/)
|
|
1066
|
+
- [Avoiding Mutable Global State in Browser JS (Josh Wulf)](https://www.joshwulf.com/blog/2020/02/avoid-global-state/)
|
|
1067
|
+
- [Why Singleton Design Pattern is Considered an Anti-pattern (GeeksforGeeks)](https://www.geeksforgeeks.org/why-is-singleton-design-pattern-is-considered-an-anti-pattern/)
|
|
1068
|
+
- [Hooks, Dependencies and Stale Closures (TkDodo)](https://tkdodo.eu/blog/hooks-dependencies-and-stale-closures)
|
|
1069
|
+
- [Optimistic Updates (TanStack Query)](https://tanstack.com/query/v4/docs/react/guides/optimistic-updates)
|
|
1070
|
+
- [useState Race Conditions and Gotchas in React (Leonardo)](https://leo88.medium.com/usestate-race-conditions-gotchas-in-react-and-how-to-fix-them-48f0cddb9702)
|
|
1071
|
+
- [Why Most React Bugs Come From State Inconsistency, Async Updates, and Race Conditions](https://medium.com/@rahulkengale1110/why-inconsistent-state-async-updates-and-race-conditions-cause-most-react-and-frontend-bugs-3bd141602e0a)
|
|
1072
|
+
- [React State Management in 2025 (Nadia Makarevich)](https://www.developerway.com/posts/react-state-management-2025)
|
|
1073
|
+
- [Global Variables and States: Why So Much Hate? (The Valuable Dev)](https://thevaluable.dev/global-variable-explained/)
|
|
1074
|
+
- [The Consequences of Using State over Cache (Arthur.place)](https://arthur.place/implications-of-cache-or-state)
|
|
1075
|
+
- [React useEffectEvent: Goodbye to Stale Closure Headaches (LogRocket)](https://blog.logrocket.com/react-useeffectevent/)
|
|
1076
|
+
- [Stop Using useEffect for Derived State (DreamerKumar)](https://medium.com/@dreamerkumar/stop-using-useeffect-for-derived-state-a-react-anti-pattern-thats-killing-your-app-s-performance-8dcb83b48805)
|