6.4 Reprodukálás

← 6. Debugging gondolkodásmód

6.4 Reprodukálható minimális példa készítése

Áttekintő

Sokszor a legjobb debug eszköz nem a debugger, nem a log, hanem az a folyamat, amikor leülsz és megpróbálod a hibát a lehető legkisebbre csupaszítani. Ez a „reprodukálható minimális példa" (angolul: minimal reproducible example, rövidítve: MRE vagy MCVE).

Ha megtanulod ezt a technikát, két dolgot fogsz tapasztalni:

  1. Az esetek felében megtalálod a hibát még mielőtt segítséget kérnél – mert a csupaszítás közben rájössz, mi okozza.
  2. A másik felében sokkal gyorsabb segítséget kapsz – mert a kollégáid, StackOverflow válaszolói, vagy az AI pontosan azt látják, ami a problémát okozza.

Ez az alfejezet megmutatja, hogyan csináld ezt hatékonyan.


Részletes leírás

Mi az a reprodukálható minimális példa?

Egy MRE három tulajdonsággal rendelkezik:

1. Reprodukálható – Bárki le tudja futtatni és látja a hibát. Nem mondod azt, hogy „nálam mindig előjön" – hanem adod a kódot, amit ha valaki futtat, ugyanazt a hibát kapja.

2. Minimális – Semmi felesleges nincs benne. Nincs benne az egész alkalmazás. Nincs benne a teljes adatbázis séma. Csak az a rész, ami a hibát okozza.

3. Önálló – Nem kell hozzá semmi más. Nem kell a te lokális adatbázisod, a te .env fájlod, a te 200 soros config fájlod. Futtatható önmagában.


Miért segít a csupaszítás?

Amikor egy alkalmazás hibásan viselkedik, általában sok minden fut párhuzamosan:

  • Middleware-ek
  • Adatbázis lekérdezések
  • Külső API hívások
  • Cache rétegek
  • Authentikáció
  • Validáció

A hiba bármelyikben lehet. Ha az egész rendszerben keresel, az olyan, mint egy 1000 darabos kirakóban keresni egy hiányzó darabot. Ha sikerül 10 darabra csökkenteni a kirakót, a hiányzó darab azonnal látszik.

A csupaszítás folyamata kényszerít arra, hogy végiggondold, mi szükséges a hibához és mi nem. Ez önmagában gondolkodásra kényszerít – és a gondolkodás a legjobb debug eszköz.


Hogyan csupaszíts le egy hibát lépésről lépésre

1. lépés: Írd le a hibát pontosan

Mielőtt nekifogsz csupaszítani, fogalmazd meg egy mondatban:

  • Mit csinál a kód?
  • Mit kellene csinálnia?
  • Mi a különbség?

Ha ezt nem tudod leírni, még nem érted a hibát eléggé. Ez az első feladat.

2. lépés: Azonosítsd a minimális belépési pontot

Ne az egész alkalmazást próbáld reprodukálni. Kérdezd meg: „Hol kell elindítanom, hogy lássam a hibát?"

Egy webalkalmazásnál ez lehet:

  • Egyetlen HTTP kérés egy endpoint felé
  • Egyetlen SQL lekérdezés
  • Egyetlen függvényhívás

3. lépés: Távolítsd el az irreleváns részeket

Vedd ki egyenként azokat a részeket, amik nem szükségesek a hiba megjelenéséhez. Minden eltávolítás után ellenőrizd:

  • Még mindig megjelenik a hiba? → Jó, folytasd.
  • Eltűnt a hiba? → Az eltávolított rész fontos. Tedd vissza, és más irányban csupaszíts.

4. lépés: Csökkentsd a függőségeket

  • Ha az adatbázis kell, csökkentsd az adatot a minimumra (1-2 sor, ami a hibát produkálja)
  • Ha külső API kell, próbáld lecserélni mockkal
  • Ha a framework kell, ellenőrizd, hogy valóban kell-e, vagy megjelenik plain kódban is

5. lépés: Ellenőrizd, hogy valóban minimális

Amikor azt hiszed, kész vagy, próbálj meg még egyet kivenni. Ha azzal is megjelenik, akkor tényleg minimális.


A rubber duck debugging kapcsolata

A „rubber duck debugging" egy közismert fogalom: magyarázd el a problémádat egy gumikacsa bábúnak (vagy bármi másnak). Sokan tapasztalják, hogy magyarázás közben megtalálják a választ.

Ez azért működik, mert:

  1. Kényszered van a pontos megfogalmazásra. Nem elég homályosan tudni, hogy „valami nem jó" – ki kell mondani, pontosan mi.
  1. Perspektívaváltást kényszerít. Amikor saját kódodat magyarázod, kénytelen vagy kívülről nézni rá.
  1. Felfedi a feltételezéseket. Magyarázás közben sokszor mondsz olyat, amit „nyilván tudottnak" vettél – és rájössz, hogy az a feltételezés hamis.

A minimális példa elkészítése ugyanez a hatás strukturáltan. Nemcsak gondolod, hanem csinálod is – és a cselekvés mélyebb megértést ad.


Mikor készíts MRE-t?

Mindig érdemes:

  • Ha több mint 20 perce nem találsz hibát
  • Ha segítséget készülsz kérni (kollégától, StackOverflow-tól, AI-tól)
  • Ha egy hiba sporadikusan jelenik meg (a csupaszítás segít megérteni a feltételt)

Különösen hasznos:

  • Ha a hiba csak egy bizonyos adatnál jelenik meg
  • Ha két rendszer interakciójában van a hiba (frontend-backend, service-service)
  • Ha a hiba „Production-on van, staging-en nincs" típusú

Hogyan adj meg MRE-t, ha segítséget kérsz

Ha StackOverflow-n, GitHub Issue-ban, Slacken vagy AI-nak adod a hibát, ezt a struktúrát kövesd:

1. Mit próbálsz elérni? (1-2 mondat)
2. Mit csináltál? (minimális kód, ami a hibát produkálja)
3. Mit vártál el?
4. Mi történt helyette? (a pontos hibaüzenet/kimenet)
5. Mit próbáltál már ki?

Ez a struktúra önmagában 10x gyorsabb választ eredményez, mint a „nem működik, mit csináljak?" kérdés.


Kódpélda: összetettből minimálisra

Eredeti szituáció: Egy webalkalmazásban az összes user lekérdezése 500 ms felett van, de csak akkor, ha a user-nek van legalább egy rendelése.

Rossz MRE (túl sok minden benne van):

# Indítsd el a teljes Django alkalmazást
# Állítsd be a PostgreSQL adatbázist
# Töltsd be a fixtures fájlt (1000+ sor)
# Jelentkezz be admin-ként
# Navigálj az /admin/users/ URL-re
# Figyeld meg, hogy lassú

Jó MRE (csak a lényeg):

Python példa:

import time
import sqlite3

# In-memory adatbázis – sem PostgreSQL, sem fixtures nem kell
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
conn.execute("CREATE TABLE orders (id INTEGER PRIMARY KEY, user_id INTEGER)")

# Minimális teszt adat
conn.execute("INSERT INTO users VALUES (1, 'Test User')")
conn.execute("INSERT INTO orders VALUES (1, 1)")

start = time.time()
result = conn.execute("""
    SELECT users.*, COUNT(orders.id) as order_count
    FROM users
    LEFT JOIN orders ON orders.user_id = users.id
    GROUP BY users.id
""").fetchall()
print(f"Idő: {(time.time() - start) * 1000:.2f} ms")
print(result)

JavaScript példa:

// Nincs szükség Express-re, szerverre, adatbázisra
// Csak a függvény, ami lassú

function calculateUserStats(users, orders) {
  return users.map(user => {
    // N+1 probléma: minden user-nél végigmegy az összes rendelésen
    const userOrders = orders.filter(o => o.userId === user.id);
    return { ...user, orderCount: userOrders.length };
  });
}

// Minimális teszt adat
const users = [{ id: 1, name: 'Test' }];
const orders = [{ id: 1, userId: 1 }];

console.time('calculate');
const result = calculateUserStats(users, orders);
console.timeEnd('calculate');
console.log(result);

PHP példa:

<?php
// Nincs Laravel, nincs Eloquent, nincs HTTP kérés
// Csak a logika, ami a problémát okozza

function calculateUserStats(array $users, array $orders): array {
    return array_map(function($user) use ($orders) {
        $userOrders = array_filter($orders, fn($o) => $o['user_id'] === $user['id']);
        return array_merge($user, ['order_count' => count($userOrders)]);
    }, $users);
}

$users = [['id' => 1, 'name' => 'Test']];
$orders = [['id' => 1, 'user_id' => 1]];

$start = microtime(true);
$result = calculateUserStats($users, $orders);
echo sprintf("Idő: %.2f ms\n", (microtime(true) - $start) * 1000);
print_r($result);

A minimális példával:

  • Bárki futtatja 5 másodperc alatt, setup nélkül
  • A probléma (N+1 vagy filter logika) azonnal látható
  • A kollégád azonnal ad javaslatot, nem kell az egész rendszert ismernie

Életszerű példák

Példa 1: A junior, aki 2 napig kereste a hibát

Márk junior fejlesztő volt, és az alkalmazásukban egy bizonyos adat esetén NullPointerException keletkezett. Két napig böngészte a 400 soros service osztályt.

Egy kolléga azt mondta: „Csináld meg a legkisebb kódot, ami ezt produkálja."

Márk nekiült: kivett mindent. Maradtak 8 sor. Az 5. sorban volt egy null check, amit csak akkor hagyott ki, ha az adat egy bizonyos formátumban érkezett. 10 perc alatt megvolt a hiba.

Tanulság: A hiba a kódban volt. A csupaszítás kényszere megmutatta.


Példa 2: StackOverflow kérdés, ami 3 napig nem kapott választ

Kati egy React komponens problémájával fordult StackOverflow-hoz. A kérdése így nézett ki:

„A komponensem nem renderel. Az adatok megvannak az API-ból, de semmi nem jelenik meg. A teljes kód a GitHub repoban van: [link]"

3 nap, 0 válasz.

Újraírta a kérdést egy 15 soros, önálló példával (useState + useEffect + adatmegjelenítés). 20 percen belül 3 válasz érkezett.

Tanulság: Az emberek nem fognak megnyitni egy egész repot a te problémád miatt. Adj nekik 15 sort, amit futtatni tudnak.


Példa 3: A rubber duck a gyakorlatban

Péter 3 óra debugolás után elment a kollégájához, és elkezdte magyarázni a problémát. A második mondatnál megállt:

„...és akkor a created_at mező alapján szűrök, amit... várj, UTC-ben tárolom, de a filter lokális időzónában kap értéket."

A kolléga még szót sem szólt. Péter visszafordult, kijavította az időzóna konverziót, és megvolt.

Tanulság: Nem a kolléga oldotta meg. A magyarázás kényszere oldotta meg. A gumikacsa azért működik, mert kényszert teremt arra, hogy pontosan megfogalmazd a problémát.


Példa 4: Production hiba csupaszítása

Egy API endpoint production-ban 504-es időtúllépést adott, staging-en nem. A fejlesztők nem tudták reprodukálni.

Ahelyett, hogy random kódot nézegettek volna, elkezdtek csupaszítani:

  • Kizárták a hálózati problémát (más endpointok gyorsak)
  • Kizárták az autentikációt (nyílt endpoint is lassú volt)
  • Kizárták a middleware-eket (közvetlen service hívás is lassú)
  • Maradt: az adatbázis lekérdezés

Production adatbázis: 500.000 sor a táblában. Staging: 50 sor. Az index hiányzott.

Tanulság: A csupaszítás nem csak kis kódpéldáknál működik. Rendszerszinten is hasznos: kizárás + szűkítés = megtalálod, hol van a probléma.


Tesztfeladatok

1. feladat – Fogalmi azonosítás

Melyik jellemzője hiányzik ebből az MRE-ből?

„Nálam mindig megjelenik a hiba, ha bejelentkezek és megnyomom a 'Mentés' gombot a profiloldalon. A hiba: TypeError: Cannot read properties of undefined. A forráskód a privát GitHub repomban van."

a) Reprodukálható
b) Minimális
c) Önálló
d) Mindkettő: minimális és önálló

Helyes válasz: d) – Sem minimális (nincs kód), sem önálló (privát repo, nem futtatható).


2. feladat – Rangsorolás

Rangsorold ezeket a hibaleírásokat reprodukálhatóság szerint (1 = legjobb, 4 = legrosszabb):

A) „Az alkalmazás lefagy. Próbálj ki mindent."

B) „Ez a kód mindig None-t ad vissza, ha az input üres string. Futtatható példa: result = process('') ahol process így néz ki: [15 soros kódrészlet]. Elvárt: []. Kapott: None."

C) „A checkout oldal néha 500-as hibát dob production-ban, de nem tudom reprodukálni."

D) „Az order service calculate_total() metódusa rossz összeget számol, ha 3+ item van a kosárban. Reprodukálható a mellékelt tesztesettel."

Helyes sorrend: B (1) → D (2) → C (3) → A (4)


3. feladat – Csupaszítási lépések

Melyik sorrendben érdemes csupaszítani egy webalkalmazás hibát, ha az /api/orders endpoint 500-ast dob?

a) Megnézem a teljes kódbázist
b) Ellenőrzöm, hogy a middleware nélkül is hibás-e az endpoint
c) Leírom pontosan: mit vártam, mit kaptam
d) Ellenőrzöm, hogy az adatbázis lekérdezés önállóan is hibás-e
e) Megnézem a stack trace-t és meghatározom a hibás sort

Helyes sorrend: c → e → b → d → a

(Először megfogalmazod, aztán a stack trace alapján szűkítesz, aztán komponensenként kizársz)


4. feladat – Rubber duck debug

Melyik állítás magyarázza legjobban, miért működik a rubber duck debugging?

a) A gumikacsa felismeri a hibát és megmutatja
b) Hangosan gondolkodva lassabban haladunk, és ez segít
c) A pontos megfogalmazás kényszere felfedi a rejtett feltételezéseket és pontatlanságokat
d) A szünet és pihenő segíti az agyat, nem maga a magyarázás

Helyes válasz: c


5. feladat – MRE értékelés

Értékeld ezt az MRE-t: megfelelő-e StackOverflow kérdésnek?

def get_user(user_id):
    users = {1: "Alice", 2: "Bob"}
    return users[user_id]

print(get_user(3))
# Kimenet: KeyError: 3
# Elvárás: None vagy üres válasz, ne exception

a) Igen, jó MRE – reprodukálható, minimális, önálló
b) Nem jó – nem reprodukálható
c) Nem jó – túl hosszú, nem minimális
d) Nem jó – nem mondja meg, mi az elvárás

Helyes válasz: a – Ez tökéletes MRE. 5 sor, futtatható, pontos elvárás, pontos hiba.


6. feladat – Csupaszítási döntés

Egy hiba csak akkor jelenik meg, ha:

  • Bejelentkeztél
  • Prémium felhasználó vagy
  • Legalább 5 rendelésed van
  • Kedden van (időzítéssel kapcsolatos valami)

Mit tegyél először a csupaszításhoz?

a) Ellenőrizd, hogy kedd nélkül is megjelenik-e (vegyük ki a nap feltételt)
b) Ellenőrizd, hogy 1 rendeléssel is megjelenik-e
c) Ellenőrizd, hogy bejelentkezés nélkül is megjelenik-e
d) Egyszerre vedd ki az összes feltételt, és nézd meg, mikor tűnik el

Helyes válasz: a – A kedd feltétel a legszokatlanabb. Ha kedd nélkül is megjelenik, az időzítés irreleváns és kivettük. Ha nem jelenik meg, akkor az időzítés az okozó – és ez nagyon szűkíti a keresési területet.

(Általános elv: a legszokatlanabb/legkülönösebb feltételt ellenőrizd először – ha kiejted és a hiba marad, gyorsan haladsz.)


7. feladat – Igaz vagy hamis

Jelöld meg, melyik állítás igaz és melyik hamis:

A) „MRE-t csak akkor érdemes készíteni, ha valakitől segítséget kérsz."
B) „Ha a csupaszítás közben megtalálod a hibát, az azt jelenti, hogy a csupaszítás felesleges volt."
C) „Egy jó MRE-ben a tesztelési adatok is minimálisak – csak annyi, amennyivel a hiba reprodukálható."
D) „A rubber duck debugging azért működik, mert a hangos gondolkodás aktiválja az agy más részeit."

Válaszok:

  • A) Hamis – a saját debuggolásodhoz is hasznos, az esetek felében te találod meg a hibát csupaszítás közben
  • B) Hamis – éppen hogy hasznos volt, mert elvégezte a célját
  • C) Igaz
  • D) Részben igaz, de a pontosabb magyarázat a pontos megfogalmazás kényszere (a c jelű válasz a 4. feladatban)

8. feladat – Helyes segítségkérés struktúra

Rendezd sorba egy hatékony hibaleírás elemeit:

a) Mit próbáltál már ki
b) Futtatható kód, ami a hibát produkálja
c) Mit vártál el
d) Mit próbálsz elérni (kontextus)
e) Mi történt helyette (pontos hibaüzenet)

Helyes sorrend: d → b → c → e → a

(Kontextus → kód → elvárás → valóság → már kipróbált dolgok)


9. feladat – Szituációs döntés

Anna 45 perce keres egy hibát egy 300 soros service osztályban. Még nem talált semmit. Mi a legjobb következő lépés?

a) Folytatja a kódolvasást – előbb-utóbb megtalálja
b) Azonnal segítséget kér a kollégájától
c) Megpróbál MRE-t készíteni – ez kényszeríti arra, hogy végiggondolja, pontosan mi szükséges a hibához
d) Végigloggolja az egész class-t, hátha kiugrik valami

Helyes válasz: c – 20-30 perc után az MRE készítés a leghatékonyabb következő lépés. Vagy megtalálja közben, vagy egy kész MRE-vel sokkal gyorsabb segítséget kap.


10. feladat – Komplex értékelés

Egy csapattársa Slacken ezt írja:

„Hé, van egy bugom. A felhasználói profil mentésekor valami elromlik. A backend PHP-ben van, Laravel framework. Tudnál segíteni?"

Mit kérnél tőle, hogy hatékonyan tudj segíteni? (Írd le 3-4 mondatban, mit kellene tartalmaznia a kérdésnek.)

Minta válasz: Kérném, hogy:

  1. Írja le pontosan a hibát – mi jelenik meg (hibaüzenet, státuszkód, napló bejegyzés)?
  2. Adjon minimális reprodukálható példát – a controller/service azon részét, ahol a hiba keletkezik, ne az egész alkalmazást
  3. Írja le az elvárt és a tényleges viselkedést
  4. Ha van stack trace, azt másolja be

Ezek nélkül nem tudom hatékonyan segíteni – az egész Laravel alkalmazásba nem látok bele, és a „valami elromlik" nem elég specifikus a debuggoláshoz.

Scroll to Top