per user-feedback nach QA-render-check aller 493 folien (overflow-detector via PIL bottom-band-darkness): 223015b/01-daten-grundlagen s.036 nyquist: H1 'Nyquist-Theorem — warum 2× reicht' → 'Nyquist — warum 2× reicht' body text gekürzt, drei fälle als * statt - (marp fragmented list) 223015b/03-inhalte-bild-audio-video s.009 RGB→Y'CbCr: H1 verkürzt, bg right 50% → 45% contain, list-marker * (Y/Cb/Cr reveal) text 'unabhängig komprimiert werden' eingedampft 223015b/03-inhalte-bild-audio-video s.031 inter-frame: H1 'Inter-Frame-Kompression — die zentrale Idee' → 'Inter-Frame — die zentrale Idee' bg right 42% → 40% contain bullets I/P/B mit * (fragmented reveal) 223015b/05-distribution-metadaten s.012 EXIF-tabelle: 8 zeilen → 5 zeilen (kamera+linse zusammengelegt, belichtung kompakt, software+bearbeitung zusammengelegt) 223015b/05-distribution-metadaten s.014 EXIF-stripping: 7 plattform-zeilen → 5 (facebook+iMessage und E-mail+slack+discord zusammen) faustregel als italic-zeile statt bold-block 223015c/01-geschichte-grundlagen-html s.006 alan turing: bg right 35% (default fill) → 33% contain (verhindert dass composite-bild bis footer reicht) 3 jahres-zeilen als * bullets (fragmented reveal) dhbw/01_web_eng s.054 counter: in 2 folien gesplittet: 'Counter — Vanilla + React' und 'Counter — Vue + Svelte' pointe 'vier frameworks, dasselbe pattern: state → UI re-rendert' am ende der 2. folie dhbw/08_best_practices s.005 .gitignore: code-block gekürzt: 6 sektionen → 5, mehrere lines pro logical group zusammengezogen (pem+key zusammen, vscode+idea zusammen) github.com/github/gitignore als markdown-link bonus: wo pädagogisch sinnvoll - durch * ersetzt (marp progressive-reveal in presenter-modus). nicht überall — nur wo bullets sequenziell aufgedeckt werden sollten. klausurfolien regeneriert (223015b 27, 223015c 19)
478 lines
9.2 KiB
Markdown
478 lines
9.2 KiB
Markdown
---
|
||
marp: true
|
||
theme: gaia
|
||
paginate: true
|
||
backgroundColor: #fff
|
||
header: "Web Engineering – DHBW Stuttgart"
|
||
footer: "Michael Czechowski – SoSe 2026"
|
||
title: Best Practices
|
||
---
|
||
|
||
<style>
|
||
:root { --color-foreground: #1a1a2e; --color-highlight: #d63384; }
|
||
section.invert { --color-foreground: #fff; }
|
||
section { font-size: 1.4rem; }
|
||
h1 { color: #a02060; }
|
||
section.invert h1 { color: #fff; }
|
||
h2 { color: #1f2937; }
|
||
pre { background: #0f0f23; color: #f48fb1; border-radius: 8px; border-left: 3px solid #d63384; }
|
||
pre code { background: transparent; color: inherit; }
|
||
code { background: #1a1a2e; color: #f48fb1; padding: 0.15em 0.4em; border-radius: 4px; }
|
||
a { color: var(--color-highlight); }
|
||
</style>
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Kapitel 8 — Best Practices
|
||
## Git · README · SOLID · 12-Factor · SemVer
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Git-Hygiene
|
||
|
||
---
|
||
|
||
# Sinnvolle Commits
|
||
|
||
**Schlecht:**
|
||
```
|
||
fix
|
||
wip
|
||
asdf
|
||
final
|
||
final2
|
||
final-final
|
||
```
|
||
|
||
**Gut:**
|
||
```
|
||
fix(auth): handle expired JWT correctly
|
||
feat(api): add /users/:id/posts endpoint
|
||
docs(readme): update Docker setup instructions
|
||
test(utils): cover edge cases in greet()
|
||
```
|
||
|
||
**Eine Commit-Message = Was + Warum, nicht Wie.**
|
||
|
||
---
|
||
|
||
# Conventional Commits
|
||
|
||
```
|
||
<type>(<scope>): <subject>
|
||
|
||
[optional body]
|
||
|
||
[optional footer]
|
||
```
|
||
|
||
**Types:**
|
||
- `feat:` neue Funktion
|
||
- `fix:` Bugfix
|
||
- `docs:` Doku
|
||
- `refactor:` Code-Restrukturierung
|
||
- `test:` Tests
|
||
- `chore:` Build/Tools
|
||
- `perf:` Performance
|
||
- `style:` Formatierung
|
||
|
||
Automatisches Changelog + SemVer-Bump möglich.
|
||
|
||
---
|
||
|
||
# .gitignore — was nie rein gehört
|
||
|
||
```
|
||
# Dependencies + Build
|
||
node_modules/
|
||
dist/
|
||
build/
|
||
.cache/
|
||
|
||
# Secrets
|
||
.env
|
||
.env.local
|
||
*.pem *.key
|
||
|
||
# Editor + OS
|
||
.vscode/ .idea/
|
||
.DS_Store
|
||
|
||
# Logs
|
||
*.log
|
||
```
|
||
|
||
Vorlagen: [github.com/github/gitignore](https://github.com/github/gitignore)
|
||
|
||
---
|
||
|
||
# Branches + PRs
|
||
|
||
```sh
|
||
# Neuer Feature-Branch
|
||
git checkout -b feat/user-profile
|
||
|
||
# Arbeit, Commits
|
||
git add .
|
||
git commit -m "feat(profile): add avatar upload"
|
||
|
||
# Push + PR
|
||
git push -u origin feat/user-profile
|
||
# → PR via GitHub/Gitea-UI
|
||
|
||
# Nach Merge: aufräumen
|
||
git checkout main
|
||
git pull
|
||
git branch -d feat/user-profile
|
||
```
|
||
|
||
**Faustregel:** kurzlebige Branches (Tage, nicht Monate).
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# README
|
||
|
||
---
|
||
|
||
# README — das Visitenkarten-Dokument
|
||
|
||
Wenn jemand euer Projekt im Browser sieht: **die README ist die erste und oft einzige Berührung.**
|
||
|
||
Schlechte README = Repo nicht verwendet.
|
||
|
||
**Eine gute README beantwortet:**
|
||
1. Was tut das Projekt?
|
||
2. Wie installiere ich es?
|
||
3. Wie starte ich es?
|
||
4. Wie teste ich es?
|
||
5. Wie deploye ich es?
|
||
6. Lizenz?
|
||
|
||
---
|
||
|
||
# README-Template
|
||
|
||
```markdown
|
||
# Mein Projekt
|
||
|
||
> Eine Zeile Beschreibung.
|
||
|
||
## Features
|
||
|
||
- ✓ etwas
|
||
- ✓ etwas anderes
|
||
|
||
## Setup
|
||
|
||
```sh
|
||
git clone https://...
|
||
cd projekt
|
||
cp .env.example .env
|
||
npm install
|
||
npm run dev
|
||
```
|
||
|
||
## Tests
|
||
|
||
```sh
|
||
npm test
|
||
npm run test:e2e
|
||
```
|
||
|
||
## Stack
|
||
|
||
Node 24, Express, SQLite, Vitest.
|
||
|
||
## License
|
||
|
||
MIT
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# SOLID — OO-Prinzipien
|
||
|
||
---
|
||
|
||
# SOLID — die fünf
|
||
|
||
| | Prinzip | Bedeutung |
|
||
|--|---------|-----------|
|
||
| **S** | Single Responsibility | jede Klasse/Modul = eine Verantwortung |
|
||
| **O** | Open / Closed | offen für Erweiterung, geschlossen für Änderung |
|
||
| **L** | Liskov Substitution | Subtyp ersetzbar durch Basistyp |
|
||
| **I** | Interface Segregation | kleine, fokussierte Interfaces |
|
||
| **D** | Dependency Inversion | abhängig von Abstraktionen |
|
||
|
||
Robert C. Martin („Uncle Bob"), 2000er.
|
||
|
||
---
|
||
|
||
# S — Single Responsibility
|
||
|
||
```javascript
|
||
// Schlecht — alles in einer Klasse
|
||
class UserManager {
|
||
validate() { /* ... */ }
|
||
saveToDb() { /* ... */ }
|
||
sendEmail() { /* ... */ }
|
||
generateReport() { /* ... */ }
|
||
}
|
||
|
||
// Besser — getrennte Verantwortlichkeiten
|
||
class UserValidator { /* ... */ }
|
||
class UserRepository { /* ... */ }
|
||
class EmailService { /* ... */ }
|
||
class ReportGenerator { /* ... */ }
|
||
```
|
||
|
||
---
|
||
|
||
# D — Dependency Inversion
|
||
|
||
```javascript
|
||
// Schlecht — direkt von konkreter Klasse
|
||
class OrderService {
|
||
charge(order) {
|
||
const stripe = new StripeClient();
|
||
stripe.charge(order.amount);
|
||
}
|
||
}
|
||
|
||
// Besser — Abhängigkeit injizieren
|
||
class OrderService {
|
||
constructor(paymentProvider) {
|
||
this.payment = paymentProvider;
|
||
}
|
||
charge(order) {
|
||
this.payment.charge(order.amount);
|
||
}
|
||
}
|
||
|
||
new OrderService(new StripeClient()); // im Production
|
||
new OrderService(new MockClient()); // im Test
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# 12-Factor App
|
||
|
||
---
|
||
|
||
# 12-Factor — die wichtigsten
|
||
|
||
| # | Faktor | Was |
|
||
|---|--------|-----|
|
||
| 1 | Codebase | ein Repo pro App, mehrere Deploys |
|
||
| 2 | Dependencies | explizit deklariert (package.json) |
|
||
| 3 | **Config** | über Environment Variables, nicht im Code |
|
||
| 4 | Backing Services | als Resources behandeln (z.B. DB-URL) |
|
||
| 6 | **Processes** | stateless |
|
||
| 9 | Disposability | schnell startbar + stoppbar |
|
||
| 12 | Admin Processes | als One-Off im selben Stack |
|
||
|
||
Heroku, 2011. Heute Industrie-Standard für Cloud-Apps.
|
||
|
||
---
|
||
|
||
# Factor 3 — Config in env
|
||
|
||
```javascript
|
||
// SCHLECHT — Hardcoded
|
||
const db = new Pool({ host: 'localhost', user: 'dev' });
|
||
|
||
// GUT — aus env
|
||
const db = new Pool({
|
||
connectionString: process.env.DATABASE_URL,
|
||
});
|
||
```
|
||
|
||
`.env` lokal, **Environment-Variablen** auf Server. Gleicher Code überall.
|
||
|
||
---
|
||
|
||
# Factor 6 — Stateless Processes
|
||
|
||
**Schlecht:** Session-State im RAM.
|
||
```javascript
|
||
const sessions = new Map(); // pro Server-Instanz!
|
||
```
|
||
→ Load-Balancer mit 2 Servern: User-Session geht verloren beim 2. Request.
|
||
|
||
**Gut:** State in DB / Redis.
|
||
```javascript
|
||
await redis.set(`session:${id}`, JSON.stringify(data));
|
||
```
|
||
|
||
Server-Restart → kein Datenverlust. Horizontale Skalierung möglich.
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# SemVer — semantisches Versioning
|
||
|
||
---
|
||
|
||
# SemVer — Format
|
||
|
||
```
|
||
MAJOR.MINOR.PATCH
|
||
1 . 4 . 3
|
||
```
|
||
|
||
| Teil | Bump bei |
|
||
|------|----------|
|
||
| **MAJOR** | breaking change (API-incompatible) |
|
||
| **MINOR** | neues Feature (rückwärts-kompatibel) |
|
||
| **PATCH** | Bugfix (rückwärts-kompatibel) |
|
||
|
||
**Pre-Release:** `1.0.0-beta.1`, `1.0.0-rc.2`.
|
||
|
||
---
|
||
|
||
# SemVer in package.json
|
||
|
||
```json
|
||
{
|
||
"dependencies": {
|
||
"express": "^5.0.0", // ≥ 5.0.0, < 6.0.0
|
||
"lodash": "~4.17.0", // ≥ 4.17.0, < 4.18.0
|
||
"react": "5.2.1", // exakt
|
||
"vite": "*" // jede (vermeiden)
|
||
}
|
||
}
|
||
```
|
||
|
||
**Faustregel:** `^` (Caret) ist Default — major-version-lock.
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Code-Reviews
|
||
|
||
---
|
||
|
||
# Constructive Code-Reviews
|
||
|
||
**Geben:**
|
||
- Konkret: Zeilennummern + Beispiel
|
||
- Wertschätzend: gut gemachte Stellen erwähnen
|
||
- Fragend statt fordernd: „Wäre `X` hier robuster?"
|
||
- Klar zwischen MUST/SHOULD/CONSIDER unterscheiden
|
||
|
||
**Nehmen:**
|
||
- Egoless: Code ≠ Person
|
||
- Frage zurück bei Unklarheit
|
||
- Bedanken für detaillierte Feedback
|
||
|
||
---
|
||
|
||
# Code-Review-Checkliste
|
||
|
||
| Bereich | Was prüfen |
|
||
|---------|------------|
|
||
| **Correctness** | macht's, was es soll? Edge-cases? |
|
||
| **Readability** | Namen, Struktur, Kommentare? |
|
||
| **Tests** | abgedeckt? Sinnvolle Assertions? |
|
||
| **Security** | Input-Validation, Secrets, Auth |
|
||
| **Performance** | N+1, Sync-IO, Memory-Leak |
|
||
| **A11y** (Frontend) | Semantik, Kontrast, Keyboard |
|
||
| **Doku** | README aktualisiert? CHANGELOG? |
|
||
|
||
---
|
||
|
||
<!-- _class: lead -->
|
||
|
||
# Doku lesen lernen
|
||
|
||
---
|
||
|
||
# Wo finde ich was?
|
||
|
||
| Ziel | Quelle |
|
||
|------|--------|
|
||
| Web-APIs (HTML, CSS, JS) | **MDN** — developer.mozilla.org |
|
||
| Node-APIs | **nodejs.org/docs** |
|
||
| npm-Package | `npmjs.com/package/<name>` + GitHub README |
|
||
| Sprach-Standards (ES) | **tc39.es** |
|
||
| Browser-Kompatibilität | **caniuse.com** |
|
||
| Fehler-Suche | Stack Overflow + GitHub Issues |
|
||
|
||
**Faustregel:** offizielle Doku zuerst, dann Stack Overflow.
|
||
|
||
---
|
||
|
||
# Stack Overflow gut nutzen
|
||
|
||
**Bevor du fragst:**
|
||
- Existiert die Frage schon? (Suche oben)
|
||
- Gibt es ein Minimal-Reproduction-Beispiel?
|
||
- Welche Versionen (Node, OS, Library)?
|
||
- Was hast du schon versucht?
|
||
|
||
**Bei der Frage:**
|
||
- Konkreter Titel (nicht "JavaScript Hilfe")
|
||
- Code-Block für Code
|
||
- Was war erwartet, was passiert
|
||
- Error-Message als Text (nicht Screenshot)
|
||
|
||
---
|
||
|
||
# AI-Assistenten (ChatGPT, Claude, Copilot)
|
||
|
||
**Pro:**
|
||
- Schneller Antworten zu allgemeinen Fragen
|
||
- Boilerplate-Code in Sekunden
|
||
- Erklärung fremden Codes
|
||
|
||
**Contra:**
|
||
- Halluziniert APIs, die nicht existieren
|
||
- Verstärkt Anti-Patterns aus dem Training
|
||
- Lernkurve geht zurück, wenn nur kopiert wird
|
||
|
||
**Faustregel:** als Sparring-Partner, nicht als Wahrheitsquelle. **Verstehen, nicht kopieren.**
|
||
|
||
---
|
||
|
||
<!-- _class: aufgabe -->
|
||
|
||
# Selbstlernen — Projekt-Hygiene-Check
|
||
|
||
Geht euer Projekt durch:
|
||
|
||
1. **README:** hat alle 6 Sektionen?
|
||
2. **.gitignore:** komplett, keine Secrets im Repo?
|
||
3. **Conventional Commits:** letzte 10 Commits durchgehen
|
||
4. **package.json:** `version`, `description`, `scripts`?
|
||
5. **Tests:** Coverage > 70 %?
|
||
6. **Dockerfile + compose.yml:** funktionieren?
|
||
7. **Environment:** `.env.example` vorhanden, `.env` gitignored?
|
||
|
||
**Bonus:** GitHub-Actions-Workflow für Tests + Build.
|
||
|
||
---
|
||
|
||
# Abschluss
|
||
|
||
**Über 8 Termine gelernt:**
|
||
1. Web Engineering — Foundation
|
||
2. CSS Extended — Layout + Animation
|
||
3. Node.js Basics — Runtime + REST
|
||
4. Node.js Advanced — async + DB + Auth
|
||
5. Testing — Unit + Integration + E2E
|
||
6. TypeScript — Typen + Schemas
|
||
7. Docker — Container + compose
|
||
8. Best Practices — Git + README + SOLID + 12-Factor
|
||
|
||
**Nächste Schritte:** eigenes Projekt für die Prüfungsleistung. Code-Abgabe + Präsentation.
|