一键导入
development-standards
Normes de développement frontend et backend pour la mise en oeuvre de projet Angular/Fastapi
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Normes de développement frontend et backend pour la mise en oeuvre de projet Angular/Fastapi
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | development-standards |
| description | Normes de développement frontend et backend pour la mise en oeuvre de projet Angular/Fastapi |
| Layer | Technology | Version | Features |
|---|---|---|---|
| Frontend | Angular | 21 | Signals, Signal Forms, Zoneless, Standalone |
| Backend | FastAPI | 0.136.0 | Async/Await, Pydantic v2 |
| UI Framework | Bootstrap | 5.3.8 | Responsive, Accessible |
| Icons | Bootstrap Icons | 1.13.1 | SVG Icons |
| Python Runtime | Python | 3.14 | Asynchronous |
| Node Runtime | Node.js | 18+ | ES2021 |
| Deployment | Docker | Multi-stage | Single Container SPA |
Les SPA Angular 21 doivent suivre les normes suivantes pour garantir la maintenabilité, les performances et la scalabilité.
Angular 21 apporte des changements majeurs:
* (dépréciées: *ngIf, *ngFor, *ngSwitch)[(ngModel)] (déprécié, utiliser des signaux et événements)FormGroup (déprécié, utiliser form et FormField de Signal Forms)@if, @for, @switch)Tous les composants doivent avoir des fichiers séparés pour template et style:
app/features/user/
├── user-list/
│ ├── user-list.component.ts ← Code TypeScript
│ ├── user-list.component.html ← Template HTML
│ └── user-list.component.css ← Styles CSS
frontend/src/app/
├── core/ # Singleton services, guards, interceptors
│ ├── services/
│ │ ├── auth.service.ts # Authentication
│ │ ├── api.service.ts # HTTP API calls
│ │ ├── theme.service.ts # Theme management
│ │ └── ...
│ ├── guards/
│ │ └── auth.guard.ts # Route protection
│ ├── interceptors/
│ │ └── auth.interceptor.ts # Add JWT tokens
│ ├── models/
│ │ ├── auth.models.ts
│ │ ├── user.models.ts
│ │ └── ...
│ └── constants/
│ └── app.constants.ts
│
├── features/ # Feature modules (lazy loaded)
│ ├── auth/
│ │ ├── login/
│ │ │ ├── login.component.ts
│ │ │ ├── login.component.html
│ │ │ └── login.component.css
│ │ └── ...
│ ├── dashboard/
│ │ ├── dashboard.component.ts
│ │ ├── dashboard.component.html
│ │ └── dashboard.component.css
│ └── ...
│
├── shared/ # Reusable components, pipes, directives
│ ├── components/
│ │ ├── header/
│ │ ├── layout/
│ │ └── ...
│ ├── pipes/
│ │ └── custom.pipe.ts
│ └── directives/
│ └── custom.directive.ts
│
├── app.config.ts # Angular configuration
├── app.routes.ts # Route definitions
├── app.component.ts # Root component
└── main.ts # Application entry point
Les projets FastAPI doivent suivre les normes suivantes pour garantir la maintenabilité, les performances et la scalabilité.
# Angular
my-component.component.ts # Composant
my-component.component.html # Template
my-component.component.css # Styles
my.service.ts # Service
my.pipe.ts # Pipe
my.directive.ts # Directive
my.guard.ts # Guard
# FastAPI
user_service.py # Service
user_models.py # Modèles Pydantic
user_routes.py # Routes
config.py # Configuration
exceptions.py # Exceptions personnalisées
// Angular/TypeScript
const MAX_RETRY = 3; // Constantes
let currentUser: User | null; // Variables
function getUserById(id: number): User; // Fonctions
class UserService { } // Classes
interface IUser { } // Interfaces
type AuthResponse = { ... }; // Types
enum Status { ACTIVE, INACTIVE } // Énumérations
// HTML templates
data-testid="user-form" // Tests
aria-label="Close modal" // Accessibilité
(click)="handleClick()" // Événements
(keydown.enter)="submitForm()" // Événements clavier
[attr.aria-expanded]="isOpen()" // Attributs dynamiques
# Python/FastAPI
MAX_RETRIES = 3 # Constantes
current_user = None # Variables
def get_user_by_id(user_id: int): # Fonctions
class UserService: # Classes
async def fetch_data(): # Fonctions asynchrones
// Angular - Error handling
try {
const user = await this.userService.getUser(id).toPromise();
this.selectedUser.set(user);
} catch (error) {
logger.error("Failed to load user", error);
this.errorMessage.set("Failed to load user. Please try again.");
}
# FastAPI - Error handling
try:
user = await self.db.get_user(user_id)
if not user:
raise NotFoundException("User", user_id)
return user
except Exception as exc:
logger.error(f"Error fetching user {user_id}: {exc}")
raise HTTPException(
status_code=500,
detail="Internal server error"
)
Tous les changements doivent respecter la configuration définie dans .pre-commit-config.yaml.
Le fichier .pre-commit-config.yaml configure les outils suivants:
Ruff remplace Black, Flake8, isort et autres (outils Python).
# Run all ruff checks/formatting
ruff check backend/ --fix # Auto-fix linting issues
ruff format backend/ # Format code
# Configuration (pyproject.toml)
[tool.ruff]
line-length = 100
target-version = "py314"
[tool.ruff.lint]
select = ["E", "F", "W", "I", "B", "C4", "UP"]
ignore = ["E501"] # Line too long is handled by formatter
Utilisé pour:
Met à jour la syntaxe des type hints Python aux standards modernes.
# Manual run (not automatic)
pre-commit run --hook-stage manual python-typing-update --all-files
# Configuration
# Python 3.14+ syntax: `str | None` au lieu de `Optional[str]`
# `list[int]` au lieu de `List[int]`
Exemples de mise à jour:
# Before (old syntax)
from typing import Optional, List
def get_users() -> Optional[List[str]]:
pass
# After (Python 3.14 syntax)
def get_users() -> list[str] | None:
pass
Formate HTML, JSON, YAML, et Markdown.
# Format frontend files
npm run prettier
cd frontend && npx prettier --write "src/**/*.{ts,html,scss}"
# Configuration (.prettierrc)
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"arrowParens": "avoid"
}
Vérifie l'orthographe et détecte les erreurs courantes.
# Check spelling in all files
codespell
# Auto-fix spelling errors
codespell --write-changes
# Configuration (.codespellrc)
skip = ./.git,*.json,*.csv,.devcontainer,.vscode
ignore-words-list = te
Hooks fournis par pre-commit pour validations basiques:
# Standard validations applied automatically
check-json # Validate JSON files
check-yaml # Validate YAML files (with --unsafe)
check-toml # Validate TOML files
check-added-large-files # Prevent committing large files
end-of-file-fixer # Ensure files end with newline
trailing-whitespace # Remove trailing spaces
mixed-line-ending # Normalize line endings
Assurez-vous que les fichiers se terminent par un saut de ligne. Supprimez les espaces de fin de ligne. Normalisez les fins de ligne.
Valide la structure YAML selon les standards.
# Check YAML files
yamllint .
# Configuration (.yamllint)
extends: default
rules:
line-length: disable
indentation:
spaces: 2
# Run all hooks on all files
prek run --all-files
# Run specific hook
prek run ruff-format --all-files
prek run prettier --all-files
# Install pre-commit hook (auto-run on git commit)
prek install
# Update hooks to latest versions
prek autoupdate
# Disable hooks for single commit (not recommended!)
git commit --no-verify
Avant chaque commit:
# 1. Format all code
cd backend && ruff format . && cd ..
cd frontend && npm run prettier && cd ..
# 2. Run all pre-commit hooks
prek run --all-files
# 3. Fix any remaining issues manually
# (ruff might have unfixable errors)
# 4. Verify no errors remain
prek run --all-files # Should pass without changes
# 5. Run tests
cd backend && python -m pytest && cd ..
cd frontend && npm run test && cd ..
# 6. Commit changes
git add .
git commit -m "feat(scope): description"
VS Code - .vscode/settings.json:
{
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
}
},
"[typescript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[html]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[yaml]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
Erreur: "Line too long"
# ❌ WRONG - Line > 100 chars
user_data = UserService.get_all_active_users_with_pending_transactions_and_notifications()
# ✅ CORRECT - Break into multiple lines
user_data = UserService.get_all_active_users_with_pending_transactions_and_notifications()
Erreur: "Import sorting"
# ❌ WRONG - Wrong import order
import os
from typing import List
import sys
from app.models import User
# ✅ CORRECT - Group: stdlib, third-party, local
import os
import sys
from typing import List
from app.models import User
Erreur: "Type hints missing"
# ❌ WRONG - No type hints
def process_data(data):
return data.strip()
# ✅ CORRECT - Complete type hints
def process_data(data: str) -> str:
"""Process user input.
Args:
data: Input string to process.
Returns:
str: Processed string.
"""
return data.strip()
❌ Ne pas utiliiser zone.js:
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
]
};
✅ Utiliser Angular en mode zoneless:
export const appConfig: ApplicationConfig = {
providers: [
provideZonelessChangeDetection()
]
};
❌ Utiliser les anciennes directives:
// WRONG - Deprecated directives
<div *ngIf="isVisible">Content</div>
<div *ngFor="let item of items">{{ item }}</div>
✅ Utiliser le nouveau control flow:
// CORRECT - Use control flow
@if (isVisible) {
<div>Content</div>
}
@for (item of items; track item.id) {
<div>{{ item }}</div>
}
❌ Ne pas utiliser les signaux:
// WRONG - Old way with property
export class MyComponent {
isLoading = false; // Not reactive
items: Item[] = []; // Not tracked
}
✅ Utiliser les signaux:
// CORRECT - Reactive with signals
export class MyComponent {
isLoading = signal(false); // Reactive state
items = signal<Item[]>([]); // Tracked changes
itemCount = computed(
() =>
// Derived state
this.items().length,
);
}
❌ Inliner trop de code HTML/CSS:
// WRONG - 200 lignes de HTML inlinées
@Component({
selector: 'app-complex',
template: `<div>... 150 lignes ...</div>`,
styles: [`... 100 lignes CSS ...`]
})
✅ Utiliser des fichiers séparés:
// CORRECT - External files
@Component({
selector: 'app-complex',
templateUrl: './complex.component.html',
styleUrls: ['./complex.component.scss']
})
❌ Utiliser any type:
// WRONG - Lose type safety
function processData(data: any): any {
return data.transform();
}
✅ Utiliser des types spécifiques:
// CORRECT - Strong typing
interface DataModel {
id: number;
name: string;
}
function processData(data: DataModel): string {
return data.name.toUpperCase();
}
❌ Oublier de se désabonner (RxJS):
// WRONG - Memory leak
export class MyComponent implements OnInit {
ngOnInit() {
this.service.data$.subscribe((data) => {
this.items = data;
});
}
}
✅ Utiliser takeUntilDestroyed ou signaux:
// CORRECT - Proper cleanup
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
export class MyComponent {
private destroyRef = inject(DestroyRef);
constructor(private service: Service) {
this.service.data$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((data) => {
this.items = data;
});
}
}
❌ Typing manquant:
def get_user(user_id):
return db.get_user(user_id)
✅ Type hints complets:
async def get_user(
user_id: int,
db: AsyncSession = Depends(get_db)
) -> UserResponse:
"""Get user by ID.
**❌ Typing déprécié:**
```python# WRONG - Old typing syntax
from typing import List, Optional
✅ Typing moderne:
def get_users() -> list[UserResponse] | None:
...
❌ Backtrace dans le retour client
# WRONG - Information leak
try:
function()
except Exception as exc:
return f"Error {exc}"
❌ Endpoints non-asynchrones:
# WRONG - Block thread
@app.get("/users")
def get_users(): # Not async!
users = db.query(User).all()
return users
✅ Endpoints asynchrones:
# CORRECT - Async all the way
@app.get("/users")
async def get_users(
db: AsyncSession = Depends(get_db)
) -> List[UserResponse]:
"""Get all users."""
users = await db.get_all_users()
return users
❌ Utiliser print() pour logs:
# WRONG - No log levels, hard to grep
@app.get("/data")
async def get_data():
print("User requested data") # Bad!
return {"status": "ok"}
✅ Utiliser logging:
# CORRECT - Proper logging
import logging
logger = logging.getLogger(__name__)
@app.get("/data")
async def get_data() -> dict:
"""Get data."""
logger.info("User requested data")
return {"status": "ok"}
❌ Pas de type hints:
# WRONG - No type safety
def create_user(user_data):
return UserService.create(user_data)
✅ Type hints complets:
# CORRECT - Full type hints
async def create_user(
user_data: UserCreate,
db: AsyncSession = Depends(get_db)
) -> UserResponse:
"""Create new user.
Args:
user_data: User creation request.
db: Database session.
Returns:
UserResponse: Created user.
Raises:
HTTPException: If user already exists.
"""
user = await db.create_user(user_data)
return user
❌ Gestion d'erreurs manquante:
# WRONG - No error handling
@app.get("/items/{item_id}")
async def get_item(item_id: int):
item = await db.get_item(item_id)
return item # Crashes if not found!
✅ Gestion d'erreurs appropriée:
# CORRECT - Proper error handling
@app.get("/items/{item_id}", response_model=ItemResponse)
async def get_item(
item_id: int,
db: AsyncSession = Depends(get_db)
) -> ItemResponse:
"""Get item by ID.
Args:
item_id: Item unique ID.
db: Database session.
Returns:
ItemResponse: Item data.
Raises:
HTTPException: If item not found.
"""
item = await db.get_item(item_id)
if not item:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Item {item_id} not found"
)
return item
❌ Hardcoder la configuration:
# WRONG - Secrets in code!
DATABASE_URL = "postgresql://user:password@localhost/db"
SECRET_KEY = "my-secret-key"
ADMIN_PASSWORD = "fixed-password"
✅ Utiliser les variables d'environnement:
# CORRECT - Load from environment
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str # From env
secret_key: str # From env
admin_password: str # From env
class Config:
env_file = ".env"
settings = Settings()
❌ Pas de validation Pydantic:
# WRONG - No validation
@app.post("/users")
async def create_user(data: dict):
# Anything goes!
user = await db.create(data)
return user
✅ Validation avec Pydantic:
# CORRECT - Strong validation
from pydantic import BaseModel, EmailStr, Field
class UserCreate(BaseModel):
username: str = Field(
...,
min_length=3,
max_length=50,
description="Username 3-50 chars"
)
email: EmailStr # Validates email format
password: str = Field(
...,
min_length=8,
description="Password min 8 chars"
)
@app.post("/users", response_model=UserResponse)
async def create_user(
user_data: UserCreate,
db: AsyncSession = Depends(get_db)
) -> UserResponse:
"""Create user with validation."""
# Only valid data reaches here
user = await db.create_user(user_data)
return user
Pour toute question concernant les normes de développement:
Pour proposer des modifications aux normes:
docs/standards-update| Version | Date | Changements |
|---|---|---|
| 1.2.0 | 2026-03-13 | Ajout section documentation |
| 1.1.0 | 2026-03-07 | Ajout section pre-commit & erreurs courantes |
| 1.0.0 | 2026-03-07 | Version initiale avec Angular 21 & FastAPI |
Dernière mise à jour: 7 Mars 2026 Statut: Production Ready ✅ Mainteneurs: Development Team