Bad Smell Audit

Run a structural bad-smell audit across the full stack. Covers apps/backend/src/, apps/bff/src/, and apps/web/. Report every find…

lmmoreira updated 27d ago
Claude CodeGeneric
View source ↗
Run a structural bad-smell audit across the full stack. Covers `apps/backend/src/`, `apps/bff/src/`, and `apps/web/`. Report every finding with file path and line number. Group findings by layer and category. At the end give a total issue count. Fix nothing — audit only.

Optional argument: `$ARGUMENTS`
- `backend` or a context path (e.g. `contexts/customer`) — restrict to backend only
- `bff` — restrict to BFF only
- `web` — restrict to web only
- blank — scan all three layers
- Append `--pr` to any of the above to scope checks to PR-changed files only (e.g. `backend --pr`, `web --pr`)

---

## PR mode (`--pr` flag)

When `--pr` is present, do **not** scan the full layer directory. Instead:

1. Compute the changed file list for the relevant layer:
```bash
# backend --pr
git diff origin/main...HEAD --name-only | grep "^apps/backend/"

# bff --pr
git diff origin/main...HEAD --name-only | grep "^apps/bff/"

# web --pr
git diff origin/main...HEAD --name-only | grep "^apps/web/"
  1. Pass this file list to the Explore agent as its explicit scope — the agent greps and reads only those files, not the full directory tree.

  2. BE-4 is skipped in --pr mode — checking for missing entity builders requires scanning the full src/test/builders/ tree against all entity files; this is a full-codebase check that the pre-PR script (Step 1, check 28) already covers for new entities added in the PR.

  3. All other checks (BE-1, BE-2, BE-3, BE-5, BE-6, BE-7, BFF-1–4, WEB-1–7) run normally but scoped to the changed file list.

If the git diff for a layer returns zero files, skip that layer entirely and report (no changed files in this layer).


Execution — parallel Explore agents

Spawn three Explore agents in parallel, one per layer. Give each agent the full check list from its corresponding section below plus its exact scope path. Request "very thorough" search breadth. Do not write the report until all three agents have returned findings.

Agent Scope Checks to pass
Backend apps/backend/src/ (full) or changed files list (--pr) Backend checks section (BE-1 through BE-7; skip BE-4 in --pr mode)
BFF apps/bff/src/ (full) or changed files list (--pr) BFF checks section (BFF-1 through BFF-4)
Web apps/web/ (full) or changed files list (--pr) Web checks section (WEB-1 through WEB-7)

If $ARGUMENTS restricts to a single layer or a specific context path, spawn only the relevant agent.


Backend checks (scope: apps/backend/src/)

BE-1. Aggregate props typed as plain primitives when a shared VO exists

Shared VOs and the primitive they replace:

  • Emailemail: string
  • PhoneNumberphone: string
  • Slugslug: string
  • Timezonetimezone: string
  • TimeOfDay → fields named open, close, opens_at, closes_at typed as string inside business_hours-like structures
  • HexColor → fields named color, primary_color, accent_color typed as string

How to find them: look for Props interfaces inside */domain/*.aggregate.ts files. Report any field that matches a known VO candidate but is typed as string or number.

BE-2. Duplicated isValidXxx / inline validation functions outside src/shared/value-objects/

Grep for:

  • function isValid outside src/shared/value-objects/
  • const isValid outside src/shared/value-objects/
  • Inline regex patterns like /^[a-z0-9-]+$/, /^#[0-9A-Fa-f]{6}$/, /@.*\./ in domain or application layer files (not in value-objects)
  • Intl.supportedValuesOf calls outside src/shared/value-objects/

BE-3. makeXxx() helpers or inline TypeORM entity construction in tests

Grep for:

  • function make in *.spec.ts or *.integration.spec.ts files
  • new XxxEntity() called directly inside a test it() or describe() block (not inside a builder class)
  • Object literals assigned to a variable of a TypeORM entity type inside test files

The fix pattern: create a XxxEntityBuilder in src/test/builders/<context>/.

BE-4. Missing XxxEntityBuilder for existing TypeORM entities

For each TypeORM entity class found in */infrastructure/entities/*.entity.ts, check whether a corresponding XxxEntityBuilder exists in src/test/builders/<context>/. Report entities that have no builder file.

BE-5. Seed file containing DDL

Check src/shared/database/seed.ts (and any other file under src/shared/database/) for:

  • CREATE TABLE, CREATE SCHEMA, DROP TABLE, DROP SCHEMA
  • ensureSchemas, createSchemas, createTable

Seeds must be data-only. Schema belongs in migrations.

BE-6. Utility functions duplicated across files (outside src/shared/utils/)

Grep for:

  • deepMerge implemented inline (not imported from src/shared/utils/deep-merge)
  • Any function body that re-implements string trimming, digit-stripping, or format conversion that already exists in a shared VO or util

BE-7. Builder fields without a withXxx() setter must be readonly (S2933)

For each *.builder.ts in src/test/builders/, find private fields initialised inline (private fieldName = ...) that have no corresponding withFieldName(...) fluent setter method. SonarCloud (S2933) flags these — a field that's never reassigned via a setter should be readonly.

Report: <file>:<line> — 'fieldName' has no setter; mark readonly


BFF checks (scope: apps/bff/src/)

BFF-1. Business logic in BFF controllers

BFF controllers must only call service methods and forward results — no domain logic inside controller method bodies. Grep for and flag:

  • Multi-branch if/else chains with more than one business condition inside controller method bodies
  • Mathematical calculations or date arithmetic inside controller method bodies
  • Domain error classes instantiated and thrown directly from controller method bodies (not via an exception filter)

BFF-2. Module/controller naming — bounded-context vs. aggregate

BFF module folders must be named after bounded conte ```

Maintain Bad Smell Audit?

Let people know it's listed here — add the badge (live metrics, light/dark aware) or a plain link to your README or docs.

[Bad Smell Audit on getagentictools](https://getagentictools.com/loops/lmmoreira-backend-pr?ref=badge)