بنقرة واحدة
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