# DHBW 2025: Web Engineering Prüfungsleistung **Maximale Punktzahl: 100 Punkte** --- ## 1. Grundanforderungen (25 Punkte) ### Projektgrundlagen (15 Punkte) - **Funktionsfähige Anwendung** (8 Punkte) - Webapp oder Backend-Anwendung läuft fehlerfrei - Kernfunktionalitäten sind in der Projektdokumentation beschrieben und testbar - **Repository** (4 Punkte) - GitHub/GitLab/CodeBerg Repository ist (öffentlich) zugänglich - Commit-Historie zeigt kontinuierliche Entwicklung - **Projektdokumentation** (3 Punkte) - Aussagekräftiger Projekttitel und Beschreibung - README.md mit Installation, Verwendung und Technologie-Stack - Kurze Erklärung der Hauptfunktionen am Anfang relevanter Dateien ### Präsentation (20 Punkte) - **Elevator Pitch** (5 Punkte) - Maximal 1 Minute - Welches Problem wird gelöst und warum wurde es vorher noch nicht gelöst? - Zielgruppe definiert - **Technische Präsentation** (5 Punkte) - PowerPoint/Folien mit Technologie-Stack - Live-Demo der Anwendung - **Projektevolution:** (5 Punkte) Jedes Projekt verläuft unterschiedlich. Sofern es zutrifft, beschreibt die unterschiedlichen Phasen des Projekts. *Wie weit seid ihr gekommen? Wo waren die Stolpersteine? Wie könnte sich eure Anwendung bei fortgesetzter Arbeit entwickeln?* 0. (Paper) Prototype 1. Make it work (PoC) 2. Make it right (MvP) 3. Make it fast (Production) --- ## 2. Clean Code Prinzipien (35 Punkte) ### Projektstruktur (10 Punkte) - **Ordnerstruktur** (5 Punkte) ``` projekt/ ├── src/ │ ├── components/ # React Components / UI │ ├── config/ # Konfigurationen, Hard-coded Strings etc. │ ├── services/ # Separitiere für-sich-stehende Funktionen Klassen, Clients etc. │ ├── utils/ # Hilfsfunktionen │ ├── main.css # Haupteinstieg in CSS Klassen, Variablen, Imports etc. │ ├── main.js # Die eigentliche Appliktion │ └── index.js # Haupteinstieg in die gesamte Applikation ├── public/ # Statische Assets, Ressourcen, Bilder etc. ├── tests/ # Test-Dateien ├── .gitignore ├── package.json └── README.md ``` - Logische Trennung von Funktionalitäten - Konsistente Benennung - **Datei-Organisation** (5 Punkte) - Ein Konzept pro Datei (Single Responsibility) - Konsistente Namenskonventionen: `kebab-case` für Dateien, `camelCase` für Variablen, `PascalCase` für Komponenten/Klassen ### Code-Qualität (15 Punkte) #### KISS Prinzip (5 Punkte) **❌ Nicht so einfach:** ```javascript const processData = (d) => d.filter(x => x.status === 'active').map(x => ({...x, processed: true})).reduce((acc, curr) => acc + curr.value, 0); ``` **✅ Einfach:** ```javascript function calculateActiveTotal(data) { const activeItems = data.filter(item => item.status === 'active'); const processedItems = activeItems.map(item => ({ ...item, processed: true })); return processedItems.reduce((total, item) => total + item.value, 0); } ``` **❌ Nicht so einfach:** ```javascript const u = users.find(u => u.id === id && u.active && !u.deleted) || null; ``` **✅ Einfach:** ```javascript function findActiveUser(users, userId) { return users.find(user => user.id === userId && user.active && !user.deleted ) || null; } ``` #### Beschreibende Namen (5 Punkte) **❌ Schlecht:** ```javascript let isActive = true; let data = []; function calc(x, y) { return x * y; } ``` **✅ Gut:** ```javascript let isMenuVisible = true; let userProfiles = []; function calculateTotalPrice(price, taxRate) { return price * taxRate; } ``` **❌ Unklar und uneindeutig:** ```javascript const btn = document.getElementById('btn'); let flag = false; function process(items) { /* ... */ } ``` **✅ Sprechende Variablennamen:** ```javascript const submitButton = document.getElementById('submit-btn'); let isFormValid = false; function validateUserInput(formData) { /* ... */ } ``` #### Funktionsverantwortlichkeiten (5 Punkte) **❌ Zu viele Verantwortlichkeiten:** ```javascript function handleUser(userData) { // Validiert Daten if (!userData.email) return false; // Speichert in DB database.save(userData); // Sendet Email sendWelcomeEmail(userData.email); // Updated UI updateUserList(); return true; } ``` **✅ Eine klare Verantwortlichkeit pro Funktion:** ```javascript function validateUserData(userData) { return userData.email && userData.name; } function saveUser(userData) { return database.save(userData); } function sendWelcomeEmail(email) { return emailService.send(email, 'welcome'); } ``` ### Kommentierung & Dokumentation (5 Punkte) **JSDoc ist optional und der beste Code muss nicht kommentiert werden.** **❌ Über-/Unterkommentiert:** ```javascript // Erhöht i um 1 i++; // Checked ob user existiert if (user) { // Macht irgendwas doSomething(); } ``` **✅ Sinnvoll kommentiert:** ```javascript /** * Handles user authentication and session management * * OR * * This file manages JWT tokens and user permissions */ function calculateCompoundInterest(principal, rate, time) { // Using compound interest formula: A = P(1 + r/n)^(nt) return principal * Math.pow((1 + rate), time); } ``` **❌ Unterkommentiert:** ```javascript function x(a,b,c,d,e) { return a*b+c-d/e; } ``` **✅ Mit JSDoc dokumentiert:** ```javascript /** * Calculates shipping cost based on weight, distance and priority * @param {number} weight - Package weight in kg * @param {number} distance - Delivery distance in km * @param {boolean} isPriority - Priority delivery flag * @returns {number} Total shipping cost */ function calculateShippingCost(weight, distance, priority) { const baseCost = weight * 2.5; const distanceCost = distance * 0.1; const priorityMultiplier = priority ? 1.5 : 1; return (baseCost + distanceCost) * priorityMultiplier; } ``` ### Live-Demo (10 Punkte) - **Fehlerfreie Demonstration** (10 Punkte) - Anwendung läuft während der Präsentation stabil - Alle Kernfunktionen werden erfolgreich vorgeführt - Ungefähr 5 Minuten Demo-Zeit --- ## 3. Accessibility & UX (25 Punkte) ### Web Accessibility (15 Punkte) #### Semantisches HTML (5 Punkte) **❌ Nicht semantisch:** ```html
Welcome
Description...
``` **✅ Semantisch korrekt:** ```html

Welcome

Description...

``` #### ARIA Labels & Alt-Texte (5 Punkte) **❌ Schlecht:** ```html chart
We don't send spam
``` **✅ Gut:** ```html Verkaufszahlen 2024: 40% Steigerung gegenüber Vorjahr
Wir versenden keine Spam-Mails
``` #### Tastaturnavigation (5 Punkte) - Alle interaktiven Elemente per Tab erreichbar - Sichtbarer Focus-Indikator - Logische Tab-Reihenfolge **❌ Nicht tastaturzugänglich:** ```html
🍔
``` ```css div.clickable { cursor: pointer; } ``` **✅ Tastaturzugänglich:** ```css div.clickable:focus { outline: 2px solid #007acc; } ``` ```html
🍔
``` ### Responsive Design (10 Punkte) #### Mobile First Approach (5 Punkte - mindestens 1 Breakpoint) ```css /* Mobile First */ .container { width: 100%; padding: 1rem; } /* Desktop */ @media (min-width: 768px) { .container { max-width: 1200px; margin: 0 auto; padding: 2rem; } } ``` #### Flexbox/Grid Layout (5 Punkte) **❌ Veraltete Layouts:** ```css .container { float: left; width: 33.33%; } .clearfix::after { content: ""; display: table; clear: both; } ``` **✅ Moderne Layouts:** ```css .card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; } .navigation { display: flex; justify-content: space-between; align-items: center; } ``` --- ## 4. Erweiterte Technologien & Innovation (15 Punkte) ### Plattformunabhängigkeit (5 Punkte) - Funktioniert auf macOS, Windows, GNU/Linux - Browser-Kompatibilität (Chrome, Firefox, Safari, Edge) **❌ Plattformspezifisch:** ```javascript // Nur Chrome navigator.clipboard.writeText(text); ``` **✅ Plattformunabhängig:** In aller Regel sind solche sog. Polyfills kaum noch eigenhändig zu implementieren. Nichtsdestotrotz gilt es die Plattformunabhängig zu prüfen und ggf. Alternativen für die Implementierung zu finden wie im folgenden Beispiel für die Zwischenablage: ```javascript // Fallback für ältere Browser function copyToClipboard(text) { if (navigator.clipboard) { return navigator.clipboard.writeText(text); } else { const textArea = document.createElement('textarea'); textArea.value = text; document.body.appendChild(textArea); textArea.select(); document.execCommand('copy'); document.body.removeChild(textArea); } } ``` ### Entwicklungs-Tools (10 Punkte) #### Package Management (4 Punkte) Sämtliche nodeJS/JavaScript Projekte sollten eine `package.json` beinhalten. `npm init` und ein Dialog öffnet sich. ```json { "name": "my-project", "scripts": { "start": "vite", "build": "vite build", "test": "jest", "lint": "eslint src/" }, "dependencies": { "react": "^18.0.0" }, "devDependencies": { "vite": "^4.0.0", "jest": "^29.0.0" } } ``` #### Build Tools & Optimierung (6 Punkte) - Vite, Webpack, oder ähnliche Tools - Optimierte Produktions-Builds - Debugger und unnötige console.log()'s entfernen - Code-Splitting - Nur Teile statt gesamte Bibliotheken importieren - Bspw. PostCSS für Entfernung von überschüssigem CSS **❌ Entwicklungs-Build:** ```javascript // Unminified, alle Dependencies geladen import * as _ from 'lodash'; // Debugging console.log('Debug info:', data); debugger; ``` **✅ Produktions-Build:** ```javascript // Minified, nur benötigte Funktionen import { debounce } from 'lodash'; // Debug-Code entfernt in Production ``` ### Zusätzliche Features (Bonus: bis zu 10 Punkte) **Wähle mindestens 2 Bereiche:** #### API Integration (5 Punkte) **❌ Ohne Error Handling:** ```javascript async function getUser(id) { const response = await fetch(`/api/users/${id}`); return response.json(); } ``` **✅ Mit Error Handling:** ```javascript async function fetchUserData(userId) { try { const response = await fetch(`/api/users/${userId}`); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } catch (error) { console.error('Fehler beim Laden der Benutzerdaten:', error); throw new Error('Benutzerdaten konnten nicht geladen werden'); } } ``` #### Local Storage / State Management (5 Punkte) **❌ Ohne Error Handling:** ```javascript function saveData(data) { localStorage.setItem('data', JSON.stringify(data)); } function loadData() { return JSON.parse(localStorage.getItem('data')); } ``` **✅ Mit Error Handling:** ```javascript function saveUserPreferences(prefs) { try { localStorage.setItem('userPrefs', JSON.stringify(prefs)); return true; } catch (error) { console.warn('Local Storage nicht verfügbar:', error); return false; } } function loadUserPreferences() { try { const data = localStorage.getItem('userPrefs'); return data ? JSON.parse(data) : getDefaultPreferences(); } catch (error) { console.warn('Fehler beim Laden der Einstellungen:', error); return getDefaultPreferences(); } } ``` #### Progressive Web App Features (5 Punkte) - Service Worker für Offline-Funktionalität - Manifest.json für App-Installation - Caching-Strategien **❌ Ohne PWA:** ```html ``` **✅ Mit PWA Features:** ```html ``` #### Testing (10 Punkte) Von Vornherein mitgedachte Tests helfen beim Code-Splitting. Um beim Testen jene Funktionen eindeutig referenzieren zu können, hilft es sie in einzelne Dateien zu verteilen und Ordnern zu gruppieren. **❌ Ohne Tests:** ```javascript // Keine Tests vorhanden function calculateTotal(price, tax) { return price + (price * tax); } ``` **✅ Mit Tests:** ```javascript // calculate.js export function calculateTotal(price, tax) { if (typeof price !== 'number' || typeof tax !== 'number') { throw new Error('Price and tax must be numbers'); } return price + (price * tax); } // calculate.test.js import { calculateTotal } from './calculate.js'; test('calculateTotal should return correct sum', () => { expect(calculateTotal(100, 0.19)).toBeCloseTo(119); }); test('calculateTotal should throw error for invalid input', () => { expect(() => calculateTotal('100', 0.19)).toThrow(); }); ``` #### Responsive Design Erweitert (5 Punkte - 2 oder mehr Breakpoints) ```css /* Mobile */ .grid { display: block; } /* Tablet */ @media (min-width: 768px) { .grid { display: grid; grid-template-columns: 1fr 1fr; } } /* Desktop */ @media (min-width: 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } } /* Large Desktop */ @media (min-width: 1440px) { .grid { grid-template-columns: repeat(4, 1fr); } } ``` #### Security Features (5 Punkte) **❌ Unsicher:** ```javascript // XSS anfällig element.innerHTML = userInput; // Unvalidierte Eingabe function search(query) { return database.query(`SELECT * FROM users WHERE name = '${query}'`); } ``` **✅ Sicher:** ```javascript // XSS-sicher durch HTML-Escaping function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } element.innerHTML = escapeHtml(userInput); // SQL-Injection sicher function search(query) { const sanitizedQuery = query.replace(/[^\w\s]/gi, ''); return database.query('SELECT * FROM users WHERE name = ?', [sanitizedQuery]); } ``` --- ## Abgabe - **Deadline:** 27.07.2025 - **Format:** GitHub/GitLab Repository Link + Live-Demo URL (falls gehostet) - **Präsentation:** 18.07.2025 - 10-15 Minuten pro Gruppe + ~5 Minuten Demo ## Tipps 1. **Beginnt früh** mit der Eingrenzung des Funktionsumfangs 2. **Testet eure Anwendung** auf verschiedenen Geräten, Betriebssystemen und Browsern 3. **Fragt bei Unklarheiten** rechtzeitig nach 4. **Fokussiert euch** auf Qualität statt Quantität 5. **Behaltet Barrieren im Blick** und vermeidet sie rechtzeitig 5. **Verwendet gute Struktur** um eure Code-Beispiele zu organisieren