Skip to main content
Maintaining a clean, scalable architecture requires consistent quality checks and adherence to proven patterns. This guide provides real quality checks extracted from the Scope Rule Architects agents.

Quality Checks Before Finalizing Decisions

Before committing any architectural decision, run through these checks:

Universal Quality Checks (All Frameworks)

Have you correctly counted feature usage?
  • Listed all files that import this component
  • Counted distinct features, not just file count
  • Remembered that multiple uses within same feature = 1 feature
  • Documented the count with comments
Example:
/**
 * ProductCard - Shared Component
 * 
 * Usage count: 2 features
 * - Shop feature: products list, search results
 * - Wishlist feature: saved items display
 * 
 * Decision: 2 features → shared/components/
 * Verified: 2024-03-15
 */
Do names follow conventions and communicate intent?
  • Component names are descriptive and clear
  • Container components match feature names (React)
  • File names use consistent casing (kebab-case or camelCase)
  • No suffixes like .component, .service unless required by framework
Good names:
✅ product-card.tsx (clear, descriptive)
✅ use-products.ts (clear hook name)
✅ auth-actions.ts (clear purpose)
Bad names:
❌ comp1.tsx (meaningless)
❌ utils.ts (too generic)
❌ stuff.tsx (unclear purpose)
Can a new developer understand what the app does from structure alone?Show the directory tree to someone unfamiliar:
features/
  auth/          # ✅ Clear: handles authentication
  shop/          # ✅ Clear: e-commerce functionality
  profile/       # ✅ Clear: user profile management
vs.
components/
  misc/          # ❌ Unclear: what's in here?
  helpers/       # ❌ Unclear: what do they help with?
  stuff/         # ❌ Unclear: what stuff?
Your structure should tell a story about business functionality, not technical implementation.
Will this structure scale as features grow?Consider these scenarios:
  • What happens when you add a new feature?
  • What if this component needs to be used by a third feature?
  • Will new developers know where to put new code?
  • Can features be moved/removed independently?
Good: Isolated features
features/
  shop/           # Self-contained, can be removed cleanly
  blog/           # Independent of shop
Bad: Tangled dependencies
components/
  product.tsx     # Used by shop, blog, wishlist, search...
                  # Removing shop feature breaks everything

Framework-Specific Quality Checks

Angular 20+ Quality Checks

All components MUST be standalone
  • No NgModules used for feature organization
  • Components don’t have standalone: true (default in Angular 20)
  • All dependencies listed in imports array
  • Using ChangeDetectionStrategy.OnPush for all components
Good:
@Component({
  selector: 'app-product-card',
  imports: [CommonModule, DatePipe],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `...`,
})
export class ProductCardComponent {}
Bad:
// ❌ Using NgModule
@NgModule({
  declarations: [ProductCardComponent],
})
export class ProductModule {}
Leverage signals appropriately for state management
  • Using signal() for mutable state
  • Using computed() for derived state
  • Using effect() sparingly for side effects
  • Exposing readonly signals publicly
Good:
export class ShopComponent {
  // Private mutable signal
  private readonly _products = signal<Product[]>([]);
  
  // Public readonly access
  readonly products = this._products.asReadonly();
  
  // Computed derived state
  readonly productCount = computed(() => this._products().length);
  
  updateProducts(products: Product[]) {
    this._products.set(products);
  }
}
Use native control flow, not structural directives
  • Using @if instead of *ngIf
  • Using @for instead of *ngFor
  • Using @switch instead of *ngSwitch
  • Using track expressions in @for
Good:
@if (loading()) {
  <div>Loading...</div>
} @else {
  @for (product of products(); track product.id) {
    <app-product-card [product]="product" />
  }
}
Bad:
<div *ngIf="loading">Loading...</div>
<div *ngIf="!loading">
  <app-product-card 
    *ngFor="let product of products; trackBy: trackByFn"
    [product]="product"
  />
</div>
Use inject() function, not constructor injection
  • Using inject() in component body
  • Avoiding constructor injection
  • No use of any type
  • Services are properly typed
Good:
export class ProductsComponent {
  private readonly productService = inject(ProductService);
  private readonly router = inject(Router);
}
Bad:
export class ProductsComponent {
  constructor(
    private productService: ProductService,
    private router: Router
  ) {}
}

Common Patterns to Follow

1. Co-location Principle

Keep related code close together
✅ Good: Co-located
features/shop/
  shop.tsx              # Main component
  components/           # Related components
  services/             # Related services
  hooks/                # Related hooks
  models.ts             # Related types

❌ Bad: Scattered
components/
  shop.tsx
services/
  shop-service.ts
hooks/
  use-shop.ts
types/
  shop.ts

2. Feature Independence

Features should be self-contained and removable
// ✅ Good: Feature imports from shared
import { Button } from '@shared/components/ui/button';
import { useLocalStorage } from '@shared/hooks/use-local-storage';

// ❌ Bad: Features importing from each other
import { ShopUtil } from '@features/shop/utils';
// Auth feature should not depend on Shop feature

3. Progressive Enhancement

Start local, move to shared when proven necessary
// Step 1: Create in local feature
// features/shop/components/product-card.tsx

// Step 2: Second feature needs it?
// Move to shared/components/product-card.tsx

// Step 3: Update imports
// - features/shop/products/page.tsx
// - features/wishlist/page.tsx

Things to Avoid

Anti-patterns that violate the Scope Rule:

1. Everything in Components Folder

❌ Anti-pattern:
components/
  LoginForm.tsx
  ProductCard.tsx
  CartItem.tsx
  UserProfile.tsx
  ...(100 more files)

✅ Better:
features/
  auth/
    login/
      components/
        login-form.tsx
  shop/
    components/
      product-card.tsx

2. Generic Utility Dumping Grounds

❌ Anti-pattern:
utils/
  helpers.ts          # What does this help with?
  utils.ts            # Redundant with folder name
  misc.ts             # Meaningless

✅ Better:
features/shop/
  utils/
    price-formatter.ts    # Clear purpose
    inventory-helpers.ts  # Clear purpose
shared/utils/
  date-formatter.ts      # Used by 2+ features

3. Premature Abstraction

❌ Anti-pattern:
// Creating shared component before it's used by 2+ features
shared/
  components/
    product-card.tsx  # Only used in shop (for now)

✅ Better:
// Keep it local until second feature needs it
features/shop/
  components/
    product-card.tsx  # Used only here

4. Framework Pattern Violations

❌ Angular:
// Using NgModules in Angular 20
@NgModule({ ... })

❌ Next.js:
// Everything as Client Component
'use client';
export default function StaticPage() { ... }

❌ Astro:
// Using client:load for everything
<StaticCard client:load />

❌ React:
// Mixing logic and presentation
export function Shop() {
  const [products, setProducts] = useState([]);
  useEffect(() => { /* fetch */ }, []);
  return <div>{/* complex UI */}</div>;
}

Continuous Quality Maintenance

Regular Architecture Reviews

Schedule periodic reviews:
  • Weekly: Quick component placement checks during code review
  • Monthly: Review if any local components should move to shared
  • Quarterly: Full architecture audit and refactoring session

Automated Checks

Set up linting rules:
// .eslintrc.js
module.exports = {
  rules: {
    // Prevent features from importing each other
    'no-restricted-imports': [
      'error',
      {
        patterns: [
          {
            group: ['@features/*/.*'],
            message: 'Features should not import from other features. Use shared/ instead.',
          },
        ],
      },
    ],
  },
};

Documentation in Code

Document architectural decisions:
/**
 * ProductCard Component
 * 
 * Placement: /shared/components/product-card.tsx
 * 
 * Used by:
 * - Shop feature (product listing, search results)
 * - Wishlist feature (saved items)
 * - Recommendations (related products)
 * 
 * Scope: 3 features (2+ rule) → Must be in shared/
 * 
 * Last reviewed: 2024-03-15
 * Reviewer: @username
 */
export function ProductCard({ product }: Props) {
  // Implementation
}

Quality Checklist Template

Use this template for every new component:
## Component: [ComponentName]

### Placement Decision
- [ ] Counted usage (X features)
- [ ] Correct directory: [path]
- [ ] Reasoning: [1 feature local / 2+ features shared]

### Framework Compliance
- [ ] Using framework-specific best practices
- [ ] Proper component type (Server/Client/Island/Standalone)
- [ ] Performance optimizations applied

### Code Quality
- [ ] Properly typed (TypeScript)
- [ ] Follows naming conventions
- [ ] Has clear, descriptive name
- [ ] Documented if complex

### Architecture
- [ ] Passes "Screaming test"
- [ ] Feature-independent (if shared)
- [ ] Future-proof structure
- [ ] No circular dependencies

Next Steps

Component Placement

Review the decision framework for placing components

Migration Guide

Restructure existing projects to follow these practices

Use the Agents

Let the agents enforce these quality checks automatically

Examples

See these practices applied in real projects

Build docs developers (and LLMs) love