blog / debugging-wie-ein-profi

Debugging wie ein Profi: Meine bewährten Techniken

Debugging ist eine Kunst

Nach Jahren des Debuggens habe ich gelernt: Die Art, wie du debuggst, kann den Unterschied zwischen 5 Minuten und 5 Stunden Fehlersuche ausmachen.

Hier sind meine bewährten Techniken, die ich täglich einsetze.

1. Die Console ist mächtiger als du denkst

Nicht nur console.log()

// :( Alle benutzen das hier
console.log(user);
console.log(orders);
console.log(config);

// :) Besser: Labeled Logging
console.log({ user, orders, config });

// :) Noch besser: Gruppierung
console.group('User Data');
console.log('User:', user);
console.log('Orders:', orders);
console.log('Config:', config);
console.groupEnd();

Console-Tricks, die ich täglich nutze

// console.table() für Arrays und Objekte
const users = [
  { name: 'Max', age: 28, city: 'Berlin' },
  { name: 'Anna', age: 32, city: 'München' },
  { name: 'Tom', age: 25, city: 'Hamburg' }
];
console.table(users);

// console.time() für Performance-Messung
console.time('API Call');
await fetchData();
console.timeEnd('API Call'); // API Call: 245ms

// console.trace() um den Call Stack zu sehen
function a() { b(); }
function b() { c(); }
function c() { console.trace('Wo bin ich?'); }
a();

// console.assert() für Annahmen
console.assert(user.age >= 18, 'User muss volljährig sein');

// Conditional Breakpoints in der Console
if (user.id === 12345) {
  debugger; // Stoppt nur für bestimmte Bedingungen
}

2. Browser DevTools wie ein Profi nutzen

Chrome DevTools Features

Logpoints (statt console.log)

  1. Rechtsklick auf eine Zeile in DevTools
  2. “Add logpoint”
  3. Eingeben: user.name, user.email
  4. Kein Code-Change nötig! 🎉

Network Tab Tricks

// Kopiere cURL command
// Rechtsklick auf Request → Copy → Copy as cURL

// Filter nach Typ
// XHR für API Calls
// JS für Scripts
// CSS für Styles

// Throttling testen
// Fast 3G, Slow 3G, Offline

Conditional Breakpoints

// Rechtsklick in DevTools auf Zeile
// "Add conditional breakpoint"
// Bedingung: user.role === 'admin'

// Stoppt nur wenn Bedingung erfüllt ist!

3. Systematisches Debugging: Der Binary Search Approach

Statt planlos console.log() zu verteilen:

// ❌ Ineffizient
function complexFunction(data) {
  console.log('Start');
  const step1 = process1(data);
  console.log('After step 1');
  const step2 = process2(step1);
  console.log('After step 2');
  const step3 = process3(step2);
  console.log('After step 3');
  // ... 20 weitere console.logs
}

// ✅ Binary Search: Halbiere den Suchbereich
function complexFunction(data) {
  const step1 = process1(data);
  const step2 = process2(step1);

  // Teste in der Mitte
  console.log('Middle checkpoint:', { step2 });

  const step3 = process3(step2);
  const step4 = process4(step3);
  // Bug vor oder nach Middle?
}

Resultat: Statt 20 Logs, brauchst du nur 4-5 mit Binary Search!

4. Reproduzierbarkeit ist der Schlüssel

Das Debug-Manifest

Bevor ich debugge, stelle ich sicher:

## Bug Report
- [ ] Kann ich den Bug reproduzieren?
- [ ] Minimal Reproducible Example erstellt?
- [ ] Edge Cases identifiziert?
- [ ] Error Messages gesammelt?
- [ ] Browser/Environment dokumentiert?

Minimal Reproducible Example

// ❌ Zu komplex zum Debuggen
// 500 Zeilen Code mit DB, Auth, etc.

// ✅ Isoliertes Beispiel
function calculateDiscount(price, userType) {
  // Nur die fehlerhafte Logik
  if (userType === 'VIP') {
    return price * 0.8; // Bug: Sollte 0.7 sein
  }
  return price;
}

console.assert(
  calculateDiscount(100, 'VIP') === 70,
  'VIP Discount sollte 30% sein'
);

5. Rubber Duck Debugging

Kein Witz - das funktioniert wirklich!

Erkläre das Problem Zeile für Zeile einer Ente (oder einem Kollegen):

“Diese Funktion nimmt einen User… dann prüft sie… oh wait, hier prüfe ich user.name aber es kommt user.username!”

80% meiner Bugs finde ich durch lautes Erklären.

6. Git Bisect für Regression Bugs

Wenn etwas früher funktioniert hat, jetzt aber nicht mehr:

# Starte Binary Search durch Git History
git bisect start
git bisect bad                    # Aktueller Commit ist buggy
git bisect good abc123           # Commit abc123 war OK

# Git checkt automatisch Commits aus
# Du testest: funktioniert es?
git bisect good   # oder
git bisect bad

# Git findet den exakten Commit, der den Bug eingeführt hat!

7. Debugging Tools für verschiedene Umgebungen

Node.js

# Chrome DevTools für Node
node --inspect server.js
# Öffne chrome://inspect

# VS Code Built-in Debugger
# F5 → Start Debugging

React

// React DevTools
// Components Tab: Props & State inspizieren
// Profiler Tab: Performance analysieren

// Warum rendert diese Component?
import { useWhyDidYouUpdate } from './hooks';

function MyComponent(props) {
  useWhyDidYouUpdate('MyComponent', props);
  // ...
}

API Debugging

# Curl mit verbose output
curl -v https://api.example.com/users

# HTTPie (schönere Ausgabe)
http -v GET https://api.example.com/users

# Postman: Collections & Environment Variables

8. Logging Best Practices für Production

// Strukturiertes Logging
const logger = {
  info: (message, meta) => {
    console.log(JSON.stringify({
      level: 'info',
      message,
      timestamp: new Date().toISOString(),
      ...meta
    }));
  },

  error: (message, error, meta) => {
    console.error(JSON.stringify({
      level: 'error',
      message,
      error: {
        message: error.message,
        stack: error.stack
      },
      timestamp: new Date().toISOString(),
      ...meta
    }));
  }
};

// Usage
logger.info('User logged in', {
  userId: user.id,
  ip: request.ip
});

logger.error('Payment failed', error, {
  userId: user.id,
  amount: payment.amount,
  method: payment.method
});

9. Performance Debugging

// Performance API
performance.mark('fetchStart');
await fetchData();
performance.mark('fetchEnd');
performance.measure('Fetch Duration', 'fetchStart', 'fetchEnd');

const measure = performance.getEntriesByName('Fetch Duration')[0];
console.log(`Fetch took ${measure.duration}ms`);

// Memory Leaks finden
if (global.gc) {
  global.gc();
  console.log('Memory:', process.memoryUsage());
}

Chrome Performance Tab

  1. Öffne DevTools → Performance
  2. Klick Record
  3. Interagiere mit der App
  4. Stop
  5. Analysiere die Flame Chart

Suche nach:

  • Lange Tasks (> 50ms)
  • Layout Thrashing
  • Memory Leaks

10. Meine Debugging Checkliste

Wenn ich stuck bin, gehe ich diese Liste durch:

✅ 1. Habe ich die Error Message wirklich gelesen?
✅ 2. Habe ich den Stack Trace analysiert?
✅ 3. Habe ich die Inputs/Outputs geloggt?
✅ 4. Funktionieren die Annahmen die ich gemacht habe?
✅ 5. Habe ich nach dem exakten Error gegoogelt?
✅ 6. Habe ich eine Pause gemacht? (Seriously!)
✅ 7. Habe ich es jemandem erklärt?
✅ 8. Ist es ein Typo? (Öfter als man denkt...)

Die goldene Regel

“Wenn du 10 Minuten stuck bist, frag um Hilfe. Wenn du 30 Minuten stuck bist, mach eine Pause.”

Manchmal ist die Lösung offensichtlich, wenn du zurückkommst.

Fazit

Gutes Debugging ist:

  1. Systematisch - nicht zufällig
  2. Reproduzierbar - isoliere das Problem
  3. Dokumentiert - schreib auf, was du versuchst hast
  4. Toolgestützt - nutze die richtigen Tools
  5. Kommunikativ - erkläre es anderen (oder der Ente 🦆)

Happy Debugging! 🐛


Was sind deine Debugging-Superpowers? Teile sie in den Kommentaren!