Skip to content
Theme
Accent
PhilosophyCase StudiesComponentsGitHub ↗
← All case studiesAngular 17.3, standalone components

review-grader

An Angular 17.3, signals-based web app.

github.com/dhruvch1244/Review-Grader

The current idiom, on purpose, with the footguns named

Standalone components only, built around signal()/computed()/effect()/input()/output() rather than RxJS/Zone.js patterns for component state — the framework's current idiom, chosen deliberately rather than defaulted into. Its own AGENTS.md doesn't just say "use signals," it names the two specific ways an LLM (or a developer fluent in older Angular) will get it wrong on this exact version.

AGENTS.md
`apps/web` is Angular 17.3, standalone components only (no NgModules), built
around signals (`signal()`, `computed()`, `effect()`, `input()`, `output()`)
rather than RxJS/Zone-based patterns for component state. Two things that
differ from older training data and are easy to get wrong:

- **NG0600**: `effect()` throws if it writes to a signal (directly, or
  indirectly - e.g. calling a service method that does) unless the effect
  is created with `effect(fn, { allowSignalWrites: true })`. ...
- **`@let` template syntax requires Angular 18.1+** and will hard-fail to
  parse on this app's Angular 17.3. ...

NG0600 — a footgun that fails silently

effect() throws internally if it writes to a signal without { allowSignalWrites: true } — but the effect just stops running past that point. From the outside, this looks like "the feature does nothing," not like a crash. The note that actually saves the next person isn't "don't write to signals in effects" in the abstract — it's "if an effect seems to have silently stopped working, check the browser console before assuming the logic itself is wrong." Silent failures are the ones worth documenting most, precisely because nothing else tells you to go look.

apps/web/src/app/shared/question-session.component.ts
      .finally(() => {
        if (!cancelled) this.loading.set(false);
      });
    // Angular disallows signal writes inside effect() by default (NG0600);
    // this effect intentionally writes loading/questions/ratings as it fetches.
  }, { allowSignalWrites: true });

@let and the version-behind-default trap

@let template syntax requires Angular 18.1+ and hard-fails to parse on this app's 17.3. The trap: some npm packages (icon libraries especially) ship newer compiled templates that emit @let, so a routine, unrelated dependency bump can break the build with an error — 'Incomplete block "let ..."' — that has no obvious connection to the version pin that actually caused it. Writing "we use Angular 17" in a doc doesn't stop anyone from hitting this; writing the exact error text and the exact trigger does, because that's what a developer or agent will actually search for.

AGENTS.md
- **`@let` template syntax requires Angular 18.1+** and will hard-fail to
  parse on this app's Angular 17.3. Some npm packages (icon libraries in
  particular) ship newer releases that emit `@let` in their compiled
  templates - pin to a version that predates it if a dependency bump starts
  failing with "Incomplete block \"let ...\"".