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:
- 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.
- 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:
- Kényszered van a pontos megfogalmazásra. Nem elég homályosan tudni, hogy „valami nem jó" – ki kell mondani, pontosan mi.
- Perspektívaváltást kényszerít. Amikor saját kódodat magyarázod, kénytelen vagy kívülről nézni rá.
- 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:
- Írja le pontosan a hibát – mi jelenik meg (hibaüzenet, státuszkód, napló bejegyzés)?
- 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
- Írja le az elvárt és a tényleges viselkedést
- 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.