Skip to content

Commit 706bbbe

Browse files
authored
docs: add language specific best practices (#6)
1 parent 24f36d8 commit 706bbbe

File tree

4 files changed

+663
-0
lines changed

4 files changed

+663
-0
lines changed
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
# Angular + TypeScript Frontend Rules
2+
3+
Concise, practical rules for building maintainable, performant, and accessible Angular apps with modern TypeScript and Angular features.
4+
5+
## Context
6+
7+
Guidance for day-to-day Angular component, template, service, and state design using the latest standalone APIs and signals.
8+
9+
*Applies to:* Angular applications and libraries (v16+), SPA/MPA frontends, design systems
10+
*Level:* Operational/Tactical
11+
*Audience:* Frontend engineers, tech leads, reviewers
12+
13+
## Core Principles
14+
15+
1. **Type safety by default:** Enable strict typing, prefer inference, and avoid unsafe escape hatches.
16+
2. **Idiomatic modern Angular:** Prefer standalone APIs, signals, native control flow, and `inject()`.
17+
3. **Simplicity and SRP:** Keep components/services small, focused, and predictable.
18+
4. **Performance-first UI:** OnPush change detection, reactive patterns, and lazy loading by default.
19+
20+
## Rules
21+
22+
### Must Have (Critical)
23+
Non-negotiable rules that must always be followed. Violation of these rules should block progress.
24+
25+
- **RULE-001:** Use strict TypeScript; avoid `any` and prefer `unknown` when type is uncertain; use type inference when obvious.
26+
- **RULE-002:** Use standalone components; do NOT set `standalone: true` in Angular decorators (it's the default).
27+
- **RULE-003:** Use signals for component/local state; use `set`/`update` only; do NOT use `mutate`.
28+
- **RULE-004:** Use `computed()` for derived state.
29+
- **RULE-005:** Use `input()` and `output()` functions instead of decorators.
30+
- **RULE-006:** Set `changeDetection: ChangeDetectionStrategy.OnPush` on all components.
31+
- **RULE-007:** Use native template control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch`.
32+
- **RULE-008:** Do NOT use `@HostBinding` or `@HostListener`; define host bindings/listeners in the `host` property of `@Component`/`@Directive`.
33+
- **RULE-009:** Do NOT use `ngClass`/`ngStyle`; use `class` and `style` bindings instead.
34+
- **RULE-010:** Prefer Reactive Forms over Template-driven Forms for new code.
35+
- **RULE-011:** Use `inject()` for dependency injection in services and where appropriate.
36+
- **RULE-012:** Use `providedIn: 'root'` for singleton services.
37+
- **RULE-013:** Implement lazy loading for feature routes.
38+
- **RULE-014:** Use `NgOptimizedImage` for static images; do NOT use it for inline base64 images.
39+
- **RULE-015:** Keep components and services single-responsibility and small.
40+
- **RULE-016:** In template control flow, do not use `as` expressions in `@else if (...)`; refactor to compute values beforehand.
41+
42+
### Should Have (Important)
43+
Strong recommendations that should be followed unless there's a compelling reason not to.
44+
45+
- **RULE-101:** Prefer inline templates for small components; use separate files for complex markup.
46+
- **RULE-102:** Keep templates simple; move complex logic to TypeScript.
47+
- **RULE-103:** Keep state transformations pure and predictable.
48+
- **RULE-104:** Use the `async` pipe to handle Observables in templates; avoid manual `subscribe()` for rendering.
49+
- **RULE-105:** Design services around a single responsibility; avoid god-services.
50+
51+
### Could Have (Preferred)
52+
Best practices and preferences that improve quality but are not blocking.
53+
54+
- **RULE-201:** Favor explicit `readonly` and immutable patterns where it clarifies intent.
55+
- **RULE-202:** Co-locate small, leaf components with their feature; promote only when reused.
56+
- **RULE-203:** Document public component inputs/outputs with concise JSDoc for design system surfaces.
57+
58+
## Patterns & Anti-Patterns
59+
60+
### ✅ Do This
61+
Concrete examples of what good implementation looks like
62+
63+
```typescript
64+
// Component with signals, computed state, OnPush, host bindings, native control flow, and class/style bindings
65+
import { Component, ChangeDetectionStrategy, input, output, signal, computed } from '@angular/core';
66+
67+
@Component({
68+
selector: 'app-counter',
69+
// no `standalone: true` (default)
70+
changeDetection: ChangeDetectionStrategy.OnPush,
71+
host: {
72+
class: 'counter',
73+
'(click)': 'onClick()'
74+
},
75+
template: `
76+
@if (count() > 0) {
77+
<span class="badge" [class.is-large]="isLarge()">{{ label() }}: {{ count() }}</span>
78+
} @else {
79+
<span class="badge is-empty">Empty</span>
80+
}
81+
<button type="button" (click)="inc()">Inc</button>
82+
`
83+
})
84+
export class CounterComponent {
85+
readonly label = input<string>('Count');
86+
readonly changed = output<number>();
87+
88+
private readonly size = input<'sm' | 'lg'>('sm');
89+
readonly count = signal(0);
90+
readonly isLarge = computed(() => this.size() === 'lg');
91+
92+
inc() {
93+
this.count.update(c => c + 1);
94+
this.changed.emit(this.count());
95+
}
96+
97+
onClick() {
98+
// handle host click via host listener in metadata
99+
}
100+
}
101+
```
102+
103+
```html
104+
<!-- Template using async pipe and native control flow -->
105+
<div>
106+
@if (user$ | async; as user) {
107+
<h3>{{ user.name }}</h3>
108+
} @else {
109+
<app-skeleton></app-skeleton>
110+
}
111+
</div>
112+
```
113+
114+
### ❌ Don't Do This
115+
Concrete examples of what to avoid
116+
117+
```typescript
118+
// Anti-patterns: decorators for IO, HostBinding/HostListener, mutate(), star control flow, ngClass/ngStyle
119+
import { Component, ChangeDetectionStrategy, Input, Output, EventEmitter, HostBinding, HostListener, signal } from '@angular/core';
120+
121+
@Component({
122+
selector: 'app-bad',
123+
standalone: true, // ❌ don't set; it's default
124+
changeDetection: ChangeDetectionStrategy.Default, // ❌ should be OnPush
125+
template: `
126+
<div *ngIf="count() > 0" [ngClass]="{ 'is-large': large }" [ngStyle]="{ color: color }">
127+
{{ label }}: {{ count() }}
128+
</div>
129+
`
130+
})
131+
export class BadComponent {
132+
@Input() label!: string; // ❌ use input()
133+
@Output() changed = new EventEmitter<number>(); // ❌ use output()
134+
135+
@HostBinding('class.bad') bad = true; // ❌ use host metadata
136+
@HostListener('click') onClick() {} // ❌ use host metadata
137+
138+
color = 'red';
139+
large = false;
140+
count = signal(0);
141+
142+
inc() {
143+
this.count.mutate(c => { c++; }); // ❌ don't use mutate
144+
this.changed.emit(this.count());
145+
}
146+
}
147+
```
148+
149+
```html
150+
<!-- Invalid control flow pitfall -->
151+
@if (getUser() as user) {
152+
<div>{{ user.name }}</div>
153+
} @else if (getAccount() as account) { <!-- ❌ invalid: as in @else if -->
154+
<div>{{ account.id }}</div>
155+
}
156+
```
157+
158+
```typescript
159+
// Avoid manual subscribe for template rendering
160+
userService.user$.subscribe(u => this.user = u); // ❌ prefer async pipe
161+
```
162+
163+
## Decision Framework
164+
165+
*When rules conflict:*
166+
1. Favor type safety, correctness, and accessibility over convenience.
167+
2. Prefer modern Angular primitives (standalone, signals, `inject()`, native control flow) over legacy patterns.
168+
3. Choose the simplest solution that satisfies SRP and performance (OnPush, lazy loading).
169+
170+
*When facing edge cases:*
171+
- Legacy or third-party constraints: wrap/adapter pattern; isolate exceptions locally.
172+
- Dynamic styling/events: prefer explicit `class`/`style` bindings and `host` metadata; avoid `ngClass`/`ngStyle` and decorators.
173+
- Template complexity: extract components/pipes or move logic to TS until templates are declarative.
174+
175+
## Exceptions & Waivers
176+
177+
*Valid reasons for exceptions:*
178+
- Interoperating with legacy modules/libraries that require deprecated patterns.
179+
- Temporary migration windows while incrementally adopting signals/standalone APIs.
180+
- Performance or security constraints that necessitate an alternative approach with measurements.
181+
182+
*Process for exceptions:*
183+
1. Document the exception, rationale, and scope in an ADR or README.
184+
2. Obtain tech lead approval.
185+
3. Time-box the exception and add a cleanup task.
186+
187+
## Quality Gates
188+
189+
- **Automated checks:** TS `strict` enabled; ESLint rules for Angular best practices; template parser rules to flag `*ngIf/*ngFor`, `@HostBinding/@HostListener`, `ngClass/ngStyle` usage; CI check for OnPush and use of `input()/output()` and `inject()` patterns.
190+
- **Code review focus:** Standalone usage (no explicit `standalone: true`), signals and computed correctness, OnPush, host metadata vs decorators, native control flow, DI via `inject()`, Reactive Forms, lazy-loaded routes, NgOptimizedImage usage, async pipe usage.
191+
- **Testing requirements:** Unit tests around computed state; component change detection with OnPush; service DI via `inject()`; route lazy loading verified via router configuration tests; template tests validating async pipe rendering.
192+
193+
## Related Rules
194+
195+
- rules/platform/typescript.instructions.md - Complementary TypeScript guidance
196+
- rules/code-quality.mdc - Actionable review comments
197+
- rules/clean-code.mdc - General maintainability practices
198+
199+
## References
200+
201+
- [Angular Signals](https://angular.dev/guide/signals) - Modern reactive state management
202+
- [Standalone APIs](https://angular.dev/guide/standalone-components) - Simplified component architecture
203+
- [Control Flow](https://angular.dev/guide/template-control-flow) - Native template control structures
204+
- [Dependency Injection and inject()](https://angular.dev/guide/di) - Modern DI patterns
205+
- [Reactive Forms](https://angular.dev/guide/forms) - Type-safe form handling
206+
- [Host Bindings/Listeners](https://angular.dev/guide/directives#host-listeners-and-host-bindings) - Component host interactions
207+
- [NgOptimizedImage](https://angular.dev/guide/image-directive) - Performance-optimized images
208+
- [Async Pipe](https://angular.dev/api/common/AsyncPipe) - Observable template integration
209+
210+
---
211+
212+
## TL;DR
213+
214+
Build with strict typing, modern Angular primitives, and simple, SRP-aligned components.
215+
216+
*Key Principles:*
217+
- Type-safe by default; prefer inference.
218+
- Use standalone, signals, `inject()`, and native control flow.
219+
- Keep things small, predictable, and OnPush.
220+
221+
*Critical Rules:*
222+
- Don't set `standalone: true`; use signals with `set`/`update` and `computed()`; use `input()`/`output()`; OnPush; no `@HostBinding/@HostListener`, no `ngClass/ngStyle`.
223+
- Use native `@if/@for/@switch`, Reactive Forms, `inject()`, `providedIn: 'root'`, lazy routes, and the `async` pipe for Observables.
224+
- Use `NgOptimizedImage` for static images; not for inline base64; avoid `as` in `@else if`.
225+
226+
*Quick Decision Guide:*
227+
When unsure, choose the modern Angular primitive that keeps templates declarative and components small; optimize for OnPush and type safety.

rules/platform/bicep.rules.mdc

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Azure Bicep Review Rules (Annotation Generation)
2+
3+
Rules for generating error annotations when reviewing Azure Bicep files against Microsoft best practices. Focused on precise, actionable, and minimal feedback for CI bots, code review tools, and LLM agents.
4+
5+
## Context
6+
7+
Guidance for producing annotations from a Bicep source file without modifying it.
8+
9+
*Applies to:* Automated Bicep reviews, CI pipelines, IDE linters, LLM-based reviewers
10+
*Level:* Operational
11+
*Audience:* Platform engineers, DevOps, reviewers, and automation authors
12+
13+
## Core Principles
14+
15+
1. Best-practices driven: Derive findings from Azure Bicep best practices; avoid subjective opinions.
16+
2. Minimal and actionable: Report only the most impactful issues with concise, fix-oriented messages.
17+
3. Precise and respectful: Pinpoint a single line per finding and respect in-source suppression signals.
18+
19+
## Rules
20+
21+
### Must Have (Critical)
22+
Non-negotiable rules that must always be followed.
23+
24+
- RULE-001: Generate error annotations based on Azure Bicep best practices (see References). Do not alter the source file.
25+
- RULE-002: Output only the top 3 most important annotations, ranked by impact and severity.
26+
- RULE-003: Each annotation must target exactly one line (single-line range only).
27+
- RULE-004: Do not generate notes or ancillary commentary; output errors only.
28+
- RULE-005: If a line starts with "#disable-next-line genaiscript", ignore the next line entirely for annotation generation.
29+
30+
### Should Have (Important)
31+
Strong recommendations to improve clarity and usefulness.
32+
33+
- RULE-101: Prefer findings that materially improve security, reliability, or correctness over stylistic nits when prioritizing top 3.
34+
- RULE-102: Provide a concise, actionable error message (≤1 sentence) that implies the fix without extra notes.
35+
- RULE-103: Deduplicate similar issues; if multiple rules trigger on the same line, emit a single combined message.
36+
37+
### Could Have (Preferred)
38+
Helpful preferences that aren’t blocking.
39+
40+
- RULE-201: When multiple candidate issues tie, prefer earlier lines and higher-severity categories (e.g., security > reliability > performance > style).
41+
- RULE-202: Keep output format consistent and machine-readable for downstream tooling.
42+
- RULE-203: Use stable identifiers (resource symbolic names, parameter names) when referencing entities in messages.
43+
44+
## Patterns & Anti-Patterns
45+
46+
### ✅ Do This
47+
Concrete examples of good output shape.
48+
49+
// Single-line, error-only, concise, respects suppression
50+
{
51+
"line": 42,
52+
"severity": "error",
53+
"message": "Avoid hardcoding location; use a parameter or resourceGroup().location."
54+
}
55+
56+
// Suppression example: if line 10 starts with
57+
// #disable-next-line genaiscript
58+
// then do not emit any annotation for line 11.
59+
60+
### ❌ Don't Do This
61+
Concrete examples to avoid.
62+
63+
// Multi-line range or more than 3 findings
64+
[
65+
{ "startLine": 5, "endLine": 9, "severity": "warning", "message": "..." },
66+
{ "line": 20, "severity": "note", "message": "..." }
67+
]
68+
69+
// Emitting notes or exceeding the top-3 limit is not allowed.
70+
71+
## Decision Framework
72+
73+
*When rules conflict:*
74+
1. Favor security/correctness issues over style or cosmetic concerns.
75+
2. Prefer higher-severity, broadly impactful issues; then choose earliest line numbers.
76+
3. If still tied, prefer de-duplicating into a single, clearer error message.
77+
78+
*When facing edge cases:*
79+
- Empty or comment-only files: produce no findings.
80+
- Multiple issues on the same line: emit one concise, combined error.
81+
- More than 3 issues: apply prioritization and emit only the top 3.
82+
83+
## Exceptions & Waivers
84+
85+
*Valid reasons for exceptions:*
86+
- Controlled experiments or diagnostics requiring more than 3 findings in a temporary debug mode.
87+
- Contractual integration that mandates a different output format.
88+
89+
*Process for exceptions:*
90+
1. Document the exception, scope, and duration in the pipeline/repo docs.
91+
2. Obtain tech lead approval.
92+
3. Time-box the exception and review periodically.
93+
94+
## Quality Gates
95+
96+
- Automated checks: Validate single-line ranges; enforce max of 3 errors; verify no notes are present; confirm suppression behavior.
97+
- Code review focus: Prioritization soundness, message clarity, respect for suppression directives, consistency of output.
98+
- Testing requirements: Unit tests for suppression, tie-breaking, de-duplication, empty files, and top-3 capping.
99+
100+
## Related Rules
101+
102+
- rules/code-quality.mdc - General guidance for actionable, minimal review comments
103+
- rules/platform/dotnet.instructions.md - Example platform-specific instruction style
104+
105+
## References
106+
107+
- Azure Bicep best practices: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/best-practices
108+
109+
---
110+
111+
## TL;DR
112+
113+
Provide only the top 3 best-practice errors for a Bicep file, each tied to exactly one line, with no notes and with suppression respected.
114+
115+
*Key Principles:*
116+
- Base findings on Bicep best practices.
117+
- Keep feedback minimal and actionable.
118+
- Respect in-source suppression and be precise.
119+
120+
*Critical Rules:*
121+
- Emit errors only; no notes.
122+
- At most 3 annotations; each is single-line.
123+
- Ignore the next line after a "#disable-next-line genaiscript" directive.
124+
125+
*Quick Decision Guide:*
126+
When in doubt, prioritize security/correctness, choose the earlier line, and keep the message short and fix-oriented.

0 commit comments

Comments
 (0)