8-qism · Amaliy tajriba·19-bob
Real loyihadan tajriba — production'da duch kelingan muammolar
O'ylab chiqarilgan misollar emas — ~170 ming qatorli, ikki yildan beri productionda bo'lgan GPS tracking + Bluetooth wearable + AI trener'li fitness ilovasida haqiqatan yuz bergan to'qqizta muammo, ularning ildiz sababi va yechimi, intervyuda qanday taqdim etish kerakligi bilan birga.
Senior daraja intervyusining eng ajratuvchi savoli odatda "eng qiyin bug haqida gapirib bering" yoki "eng murakkab arxitektura qaroringiz haqida aytib bering" shaklida keladi. Bu savolga umumiy, mavhum javob ("bir marta murakkab bug topdim, uni tuzatdim") intervyuerni ishontirmaydi — u aniq texnik detallarni, ildiz sababni qanday topganingizni va nima o'rganganingizni eshitmoqchi.
Bu bob boshqa boblardan farqli — bu yerda nazariya emas, real production ilovada (GPS orqali faoliyatni kuzatadigan, Bluetooth orqali ulanadigan fitness bilakuzuk bilan ishlaydigan va veb orqali renderlanadigan video-asosidagi AI trener moduliga ega, ikki yildan beri ishlab turgan, o'nlab minglab qatorli kod bazasi) haqiqatan yuz bergan to'qqizta muammo tasvirlangan — har biri muammo → ildiz sababni topish → yechim → umumiy dars tuzilishida. Maqsad — sizga tayyor "hikoya" berish emas, balki shu darajadagi chuqurlikda o'z tajribangizni qanday tasvirlashni o'rgatish.
1. Fon — qanday tizim haqida gap ketyapti#
Kontekst uchun: bu — GPS bilan yugurish/yurish/velosiped faoliyatini yozib oluvchi, Bluetooth orqali doimiy sog'liq ko'rsatkichlarini (yurak urishi, uyqu, qadam) sinxronlaydigan bilakuzuk bilan ishlaydigan, va kamera orqali mashq qildiradigan AI trener moduliga ega fitness ilovasi. Murakkablik widget daraxtida emas — u uchta joyda jamlangan: (1) ikki-isolatali GPS kuzatuv quvur liniyasi, (2) Bluetooth + fon rejimidagi sinxronizatsiya stack'i, (3) veb va native kod orasidagi handoff. Qolgan hammasi — oddiy Cubit-per-screen CRUD ilova.
Bu bobdagi har bir holat aynan shu uch og'ir joydan biriga tegishli — chunki intervyuda ham aynan shunday joylar so'raladi, oddiy CRUD ekran haqida emas.
2. Ikki background isolate, bitta SQLite fayl#
Muammo. GPS kuzatuv UI isolate'da emas, alohida fon isolateda ishlaydi (har bir platforma o'zining fon xizmati orqali — Android'da foreground service, iOS'da background service). Ikkala platforma implementatsiyasi ham UI isolatening o'zi bilan birga, bitta xuddi shu SQLite faylga yozadi — ya'ni istalgan paytda ikkita (fon isolate + UI isolate) mustaqil ulanish bitta faylga murojaat qilishi mumkin.
Bu — klassik "bir nechta yozuvchi bitta fayl" concurrency xavfi, lekin oddiy
mutex bilan yechib bo'lmaydi, chunki ikkala tomon turli Dart isolateda
(demak turli xotira, oddiy Lock obyekti ishlamaydi — isolate'lar orasida
qulf ulashib bo'lmaydi).
Ildiz sabab va yechim. Yechim qulflashda emas — yozish shaklida: har
bir tick uchun yozish bitta atomik statement (INSERT bitta qatorga)
bo'lishi shart, hech qachon ko'p qadamli tranzaksiya (masalan "avval o'qi, keyin
hisobla, keyin yoz") emas. SQLite'ning o'zi bitta statement darajasida atomik
kafolat beradi (WAL rejimida bir nechta yozuvchi navbatga turadi, lekin hech
qachon yarim yozilgan qator qolmaydi) — muammo faqat ko'p qadamli, "o'qi →
o'zgartir → yoz" turidagi operatsiyalarda paydo bo'ladi, chunki ikkinchi
isolate aynan shu oraliqda o'z yozuvini kiritishi mumkin.
3. "Reply already submitted" — native callback poyga holati#
Muammo. Platform channel orqali (MethodChannel) native tarafdan Dart'ga
natija qaytariladigan joyda, ilova vaqti-vaqti bilan (ayniqsa fon rejimida
uzoq ishlagandan keyin) qulab tushardi — IllegalStateException: Reply already submitted xatosi bilan.
Ildiz sababni topish. Native kod ikkita mustaqil yo'l orqali javob
yuborardi: (1) haqiqiy natija kelganda SDK callback'i orqali, (2) agar SDK
javob bermasa, xavfsizlik uchun qo'yilgan bir necha soniyalik timeout orqali.
Ikkala yo'l ham "javob yuborilganmi" degan bayroqni turli oqimlarda
(bittasi SDK'ning o'z ichki thread'ida, ikkinchisi asosiy UI thread'ida)
tekshirar va o'rnatardi — bu klassik check-then-act race condition: ikkala
oqim deyarli bir vaqtda "hali yuborilmagan" deb o'qiydi, ikkalasi ham javob
yuboradi, ikkinchisi esa MethodChannel.Resultning "bir marta javob"
qoidasini buzadi va exception otadi.
Yechim. Ikkala yo'l ham endi bitta Handler/Looperga (ya'ni bitta oqimga) yig'ilgan — tekshirish va o'rnatish endi hech qachon parallel emas, ketma-ket bajariladi, shuning uchun poyga holati fizik jihatdan mumkin emas.
4. SQLite CursorWindow chegarasi#
Muammo. Dastlabki loyihada butun GPS trekni (yuzlab-minglab nuqta) bitta JSON qator sifatida bitta katakka saqlash rejalashtirilgan edi — sodda va tezkor ko'rinardi. Uzoq (masalan marafon) faoliyatlarda ilova o'sha yozuvni o'qishga uringanda qulardi.
Ildiz sabab. Android'ning SQLite CursorWindowi bitta qatorni o'qishda
~2 MB chegaraga ega — uzun JSON matni bu chegaradan osongina oshib ketadi.
Bu — "ma'lumotlar bazasi tanlovi xato" emas, sxema dizayni xato: bitta
katakka cheksiz o'sadigan massivni tiqishtirish har doim shu turdagi
chegaraga uchraydi, qaysi DB dvigateli ishlatilishidan qat'iy nazar.
Yechim. Normal shakl: activities (metadata, kichik, tez-tez yangilanadi)
va locations (bitta qator = bitta GPS nuqtasi, PRIMARY KEY (activityId, seq), ON DELETE CASCADE) — ikkita alohida jadval. Bunga o'tishda yana ikkita
kutilmagan to'siq chiqdi (§5 va pastdagi eslatma): PRAGMA foreign_keys = ON
har bir ulanishda alohida yoqilishi shart — aks holda ON DELETE CASCADE
hech qanday xatosiz, jimgina ishlamay qoladi (bu — SQLite'ning ma'lum,
hujjatlashtirilgan xatti-harakati, lekin ko'p dasturchi buni bilmaydi).
5. INSERT OR REPLACE va kaskadli o'chirish falokati#
Muammo. Normalizatsiyadan keyin, activities jadvalidagi metadata qatorini
yangilash uchun INSERT OR REPLACE ishlatilgan edi ("agar mavjud bo'lsa —
almashtir" degan qulay semantika). Natija: har safar metadata yangilanganda
(masalan progress bar uchun masofa/vaqt), shu activity'ning barcha GPS
nuqtalari yo'qolib qolardi.
Ildiz sabab. INSERT OR REPLACE SQL darajasida DELETE + INSERT
sifatida bajariladi — u "yangilash" emas. Parent qator o'chirilganda, unga
bog'langan ON DELETE CASCADE darhol ishga tushadi va barcha bola
qatorlarni (bu yerda: locationsdagi barcha nuqtalarni) o'chiradi — hatto
yangi qator xuddi shu ID bilan bir zumda qayta yaratilsa ham, kaskad
allaqachon ishlagan bo'ladi.
Yechim. Oddiy UPDATE — u qatorni o'zgartiradi, o'chirib-qayta
yaratmaydi, shuning uchun kaskad ishga tushmaydi.
6. Offline-first sinxronizatsiya navbati#
Muammo. Foydalanuvchi tunnel/metro/tog'da yugurib, internetsiz holatda faoliyatni tugatsa, yozuv backend'ga qanday yetib borishi kerak — va agar u ikki marta yuborilsa (masalan ilova qayta ishga tushganda retry logikasi ikki marta ishlasa), backend'da dublikat yaratilmasligi kerak.
Yechim tuzilishi: tugagan faoliyat avval mahalliy holatda finished deb
belgilanadi (offline bo'lsa ham), so'ng alohida sinxronizatsiya navbati
(SyncManager) uni backend'ga yuborishga javobgar:
Connectivity-triggered
Navbat internet paydo bo'lganda avtomatik ishga tushadi (pollingda emas, tarmoq holati o'zgarishi hodisasiga obuna bo'lish orqali) — batareyani behuda sarflamaslik uchun.
Exponential backoff
Muvaffaqiyatsiz urinishdan keyin keyingi urinish oralig'i eksponensial o'sadi — server vaqtincha band bo'lganda uni "so'rov to'foni" bilan yana og'irlashtirmaslik uchun.
Single-run mutex
Bir vaqtning o'zida faqat bitta sinxronizatsiya-yugurishi bo'lishi mumkin — aks holda tarmoq qaytib kelgan zahoti bir nechta parallel yugurish bir xil yozuvni bir necha marta yuborishi mumkin edi.
uniqueId bo'yicha deduplikatsiya
Har bir yozuv mijoz tomonida generatsiya qilingan, o'zgarmas noyob ID tashiydi. Backend shu ID bo'yicha idempotent qabul qiladi — ya'ni tarmoq uzilib, mijoz "yuborildimi yo'qmi" bilmay qayta yuborsa ham, backend dublikat yaratmaydi.
7. Muvaffaqiyatsizlikni noto'g'ri domain qiymati bilan ifodalash#
Muammo. Foydalanuvchilarga haqiqiy batareya darajasi 70-80% bo'lsa ham, vaqti-vaqti bilan "Braslet o'chay deb qoldi — atigi 0%!" degan yolg'on bildirishnoma kelib turardi.
Ildiz sabab. Batareya o'qish funksiyasining bir necha muvaffaqiyatsizlik
yo'li ("SDK javob bermadi", "xato otdi", "timeout") barchasi 0 qiymatini
qaytarardi — ya'ni "men bilmayman" holati "batareya 0%" degan haqiqiy domain
qiymati bilan bir xil kod yo'lidan o'tardi. Bildirishnoma tizimi bu 0ni
haqiqiy o'lchov deb qabul qilib, kritik ogohlantirish chiqarardi.
Yechim. Funksiya imzosi Int dan Int? ga o'zgartirildi — barcha
muvaffaqiyatsizlik yo'llari endi null qaytaradi, hech qachon 0. Chaqiruvchi
tomonlar allaqachon if (battery != null) shaklida yozilgani uchun bu
o'zgarish ularning birortasiga ham tegmadi — ular shunchaki "noma'lum"
holatini to'g'ri qabul qila boshladi.
8. WebView + native SDK gibrid arxitekturasi#
Muammo. AI trener moduli Flutter'da emas — u backend'dan render qilingan
veb sahifa, WebView ichida ochiladi va kamera orqali mashq qildiruvchi native
computer-vision SDK'ni JavaScript ko'prigi orqali boshqaradi. Savol: bu ikki
dunyo (veb va native) orasida qaror qabul qilish mantig'i qayerda turishi
kerak?
Arxitektura qarori. Qaror butunlay backend/veb tomonda — qaysi komponent ochiladi, qaysi foydalanuvchi uchun, qaysi sessiya bilan — hammasi bitta JSON xabarda serverdan keladi. Flutter ilovasi faqat ijrochi: u xabarni tasdiqlaydi (kerakli maydonlar bormi) va aytilganini bajaradi, hech qachon o'z holatiga qarab qaror qabul qilmaydi.
Nega bu muhim. Agar Flutter tomoni ham o'z qoidalarini qo'shsa (masalan "agar foydalanuvchi X tarifda bo'lsa, Y komponentni och"), ikki tizim (backend va mobil) mantig'i asta-sekin ajralib keta boshlaydi — yangi mobil versiya chiqmaguncha backend'dagi o'zgarish mobilga yetib bormaydi. "Web hammasini hal qiladi, app faqat bajaradi" qoidasi bu muammoni butunlay yo'qotadi: yangi biznes-mantiq backend deploy'i bilan darhol barcha foydalanuvchilarga yetadi, App Store/Play Market ko'rib chiqishini kutmasdan.
Bunga qo'shimcha, xato boshqaruvi ham qat'iy: noto'g'ri/to'liqsiz xabar hech qachon crash yoki dialog chiqarmaydi — jimgina e'tiborsiz qoldiriladi (no-op). Bu — tashqi (backend nazorat qilinadigan bo'lsa ham, texnik jihatdan "ishonchsiz chegara" hisoblanadigan) manbadan keladigan xabarlarni qayta ishlashning xavfsiz standart qoidasi.
9. O'chirilgan (retired) kod hamon xavfli bo'lishi mumkin#
Muammo. AI trener modulining eski (v1, native, webview'siz) versiyasi yangi arxitekturaga o'tilgach ham kod bazasida hali ham kompilyatsiya bo'ladigan holatda qolib ketgan — hech qaysi marshrutdan erishib bo'lmaydi, lekin faylllar o'chirilmagan.
Nega bu xavfli. Bu eski kod eski (yangi versiyada endi ishlatilmaydigan) argument turini kutadigan marshrutga hali ham push qiladi — agar kelajakda kimdir tasodifan (masalan eski havola/deep link orqali, yoki kodni ko'chirishda) shu eski yo'lga tegib ketsa, runtime xatosi chiqadi. Bundan tashqari, eski modulning analytics chaqiruvlari ham shu daraxtda joylashgan edi — ular yangi arxitekturaga ko'chirilmagan, shuning uchun ba'zi analytics hodisalari sezilmasdan yig'ilishni to'xtatgan (kod compile bo'lgani uchun buni compiler ham, testlar ham ushlamaydi).
Dars. "Ishlatilmayotgan kodni keyin tozalayman" degan qaror — nolga
teng bo'lmagan xavf tashiydi: u kompilyatsiya bo'lganda "hali tirik" bo'lib
ko'rinadi, lekin aslida hech kim uni tekshirmaydi. Feature flag/route
o'zgarganda, eski yo'lni o'sha paytning o'zida olib tashlash (yoki hech
bo'lmaganda aniq @Deprecated/build xatosi bilan belgilash) keyingi safar
"nega bu ishlamayapti" debug qilishga sarflanadigan soatlarni tejaydi.
10. Buni intervyuda qanday taqdim etish kerak#
Yuqoridagi har bir holat — STAR (Situation, Task, Action, Result) strukturasida tabiiy ravishda joylashadi:
- S
- SituationQisqa kontekst: qanday tizim, nima kuzatilgan (masalan 'foydalanuvchilar yolg'on kritik-batareya bildirishnomasi olayotgan edi')
- T
- TaskSizning vazifangiz aniq nima edi — ildiz sababni topish, yoki tuzatishni loyihalash
- A
- ActionAniq texnik qadamlar: qanday debug qildingiz, nimani topdingiz, nima uchun aynan shu yechimni tanladingiz (muqobil variantlarni ham eslatish plyus)
- R
- ResultO'lchanadigan natija va undan olingan umumiy dars — keyingi safar shu turdagi muammoni qanday oldindan oldini olasiz
Eng katta xato — faqat "Result"ni aytish ("men tuzatdim, endi ishlayapti"). Intervyuer "Action" qismini eshitmoqchi: siz qanday ildiz sababni topdingiz (log o'qidingizmi, reproduce qildingizmi, kodni statik o'qib chiqdingizmi), va nega aynan shu yechimni (masalan "qulflash" o'rniga "atomik operatsiya dizayni") tanladingiz — bu qism sizning fikrlash jarayoningizni ko'rsatadi, natija esa faqat tasdiqlaydi.
Tez-tez so'raladigan intervyu savollari#
Savol: Ikkita mustaqil background isolate bitta SQLite faylga yozayotganda qanday muammolarga duch kelish mumkin, va buni qanday oldini olasiz?
Javob: Asosiy xavf — bir nechta yozuvchi orasidagi poyga holati va yarim
yozilgan/ziddiyatli holatlar. Isolate'lar xotira ulashmagani uchun oddiy
Lock/mutex obyekti ishlamaydi. Eng ishonchli yechim — har bir yozishni
bitta atomik SQL statementga qisqartirish (masalan bitta INSERT), hech
qachon "o'qi → hisobla → yoz" ko'p qadamli tranzaksiya qilmaslik — SQLite bitta
statement darajasida atomiklikni o'zi kafolatlaydi. Agar ko'p qadamli
operatsiya muqarrar bo'lsa, WAL rejimi + tegishli izolyatsiya darajasini
o'rganish kerak, lekin birinchi va eng oddiy yechim — operatsiyani shunday
qayta loyihalashki, u tabiatan atomik bo'lsin.
Savol: MethodChannel orqali native callback ikki marta chaqirilib, "Reply already submitted" xatosi bilan ilova qulasa, buni qanday debug qilasiz?
Javob: Avval qayerda ikkita mustaqil yo'l bir xil natijani qaytarishi mumkinligini qidiraman — odatda bu "haqiqiy SDK javobi + xavfsizlik timeout'i" kombinatsiyasi. Keyin har ikkala yo'l qaysi oqimda ishlashini tekshiraman: agar ular turli thread'larda bo'lsa (masalan SDK'ning ichki callback thread'i va asosiy UI thread'i), demak "javob yuborilganmi" bayrog'i tekshirilib-o'rnatilishi orasida poyga bor. Tuzatish — ikkala yo'lni bitta Handler/Looper (bitta ketma-ket oqim) ostiga jamlash, shunda tekshirish va o'rnatish hech qachon parallel bo'lmaydi.
Savol: SQLite'da bitta katakka katta/o'suvchi ma'lumot (masalan uzun massiv) saqlash nega yomon amaliyot?
Javob: Ikki sabab: (1) platformaga bog'liq amaliy chegaralar bor —
masalan Android'ning CursorWindowi bitta qatorni o'qishda ~2 MB chegaraga
ega, uzun JSON bu chegaradan tez oshib ketadi; (2) bunday sxema
normallashtirilmagan — bitta "element qo'shish" operatsiyasi butun katta
qatorni qayta yozishni talab qiladi, bu ham sekin, ham concurrency xavfini
oshiradi. To'g'ri yechim — alohida jadval, bitta qator = bitta element,
tegishli foreign key va indeks bilan.
Savol: INSERT OR REPLACE bilan UPDATE orasidagi amaliy farq nima, va bu farq qachon muhim bo'lib qoladi?
Javob: INSERT OR REPLACE SQL semantikasi bo'yicha avvalgi qatorni
o'chiradi, so'ng yangisini kiritadi (garchi natija tashqi ko'rinishda
"yangilangan qatorga" o'xshasa ham). Bu farq unchalik ahamiyatsiz tuyulishi
mumkin, lekin agar shu jadvalda ON DELETE CASCADE bilan bog'langan bola
jadval bo'lsa — o'chirish bosqichi kaskadni ishga tushiradi va barcha bola
qatorlarni yo'q qiladi, garchi bir zumda ota qator qayta yaratilsa ham.
UPDATE esa qatorni joyida o'zgartiradi, hech qanday o'chirish sodir
bo'lmaydi — kaskad ishga tushmaydi. Qoida: agar jadvalda foreign key
munosabatlari bo'lsa, INSERT OR REPLACEdan har doim ehtiyot bo'ling.
Savol: Offline-first mobil arxitekturada bir xil yozuvning ikki marta yuborilishini (masalan tarmoq uzilib qayta urinilganda) qanday oldini olasiz?
Javob: Server tomonda idempotentlikni ta'minlash orqali — mijoz har
bir yozuv uchun o'zgarmas, noyob uniqueId generatsiya qiladi (masalan
UUID), va backend shu ID bo'yicha "men buni allaqachon qabul qilganman"ni
biladi: takroriy so'rov kelsa, yangi yozuv yaratmaydi, shunchaki muvaffaqiyat
qaytaradi. Bu — tarmoqning tabiiy noaniqligini ("so'rov ketdimi, lekin javob
yo'qoldimi?") hal qilishning yagona ishonchli yo'li, chunki mijoz tarafida bu
noaniqlikni hech qachon 100% aniqlab bo'lmaydi.
Savol: Nega funksiya xatoni ifodalash uchun 0/-1 kabi "sentinel qiymat" o'rniga null/Result turini qaytarishi kerak?
Javob: Chunki sentinel qiymat ko'pincha domenning haqiqiy, mumkin
bo'lgan qiymati bilan to'qnashadi — "batareya 0%" haqiqatan sodir bo'lishi
mumkin bo'lgan holat, shuning uchun "men o'lchay olmadim" ma'nosidagi 0
undan farqlanmaydi. Chaqiruvchi tomon bu ikkalasini ajrata olmaydi va
noto'g'ri, ba'zan zararli qarorlar qabul qiladi (masalan yolg'on kritik
bildirishnoma). null/Result<T> turi bu ikki holatni tip darajasida
ajratadi — kompilyator sizni har ikkala holatni ham ko'rib chiqishga
majburlaydi.
Savol: Veb (WebView) va native kod orasidagi gibrid arxitekturada biznes-mantiqni qayerga joylashtirasiz — nega?
Javob: Iloji boricha serverga yaqinroq (bu holatda — webga). Sabab — deploy tezligi va bitta haqiqat manbai: server/veb kod o'zgarishi darhol barcha foydalanuvchilarga yetadi, mobil kod o'zgarishi esa App Store/Play Market ko'rib chiqishini talab qiladi (bir necha kun). Agar qaror qabul qilish mantig'i ikkiga bo'linsa (bir qismi mobilda, bir qismi serverda), ular vaqt o'tishi bilan drift qiladi — eski mobil versiyalar eski mantiqni ishlatib, yangi versiyalar bilan nomos xatti-harakat ko'rsatadi. Mobil tomon "aqlli ijrochi" emas, "ishonchli ijrochi" bo'lishi kerak: nima aytilsa, shuni tekshirib bajaradi, o'zi qaror qabul qilmaydi.
Savol: Ishonchsiz tashqi manbadan (masalan WebView'dan JS orqali) kelayotgan xabarni qayta ishlashda qanday himoya qoidalariga amal qilasiz?
Javob: Eng muhim qoida — noto'g'ri/to'liqsiz xabar hech qachon crash yoki bezovta qiluvchi dialog chiqarmasligi kerak, jimgina e'tiborsiz qoldirilishi (no-op) kerak. Bundan tashqari: har bir majburiy maydonni aniq tekshirish (null-check), noma'lum turdagi xabarlarni rad etish, va xabar formatini versiyalash (agar kelajakda backend formatni o'zgartirsa, eski app versiyasi yangi formatni tanib, xavfsiz e'tiborsiz qoldira olishi kerak). Bu — hatto "ishonchli" backend bo'lsa ham, u texnik jihatdan tashqi (mobil kod nazorat qilmaydigan) chegara hisoblanadi.
Savol: Kodning bir qismi endi marshrutlardan erishib bo'lmaydigan (unreachable) bo'lib qolsa, uni darhol o'chirmaslik qanday xavf tug'diradi?
Javob: Kompilyatsiya bo'ladigan, lekin ishlatilmaydigan kod "xavfsiz o'lik" ko'rinadi, lekin aslida emas: (1) u eski tип/argument shaklini talab qilishi mumkin, va agar kimdir tasodifan (eski deep link, noto'g'ri ko'chirish) unga tegib ketsa, runtime xatosi chiqadi; (2) agar o'sha kod ichida side-effect'lar (masalan analytics chaqiruvlari) bo'lsa, ular yangi arxitekturaga ko'chirilmagan bo'lishi mumkin — natijada funksionallik sezilmasdan "yo'qoladi", chunki hech qanday test yoki compiler buni ushlamaydi (kod compile bo'ladi, shunchaki chaqirilmaydi). Qoida: feature eskirganda, uni o'sha paytning o'zida o'chiring yoki hech bo'lmaganda build xatosi chiqaradigan tarzda belgilang — "keyinroq tozalayman" deb qoldirilgan kod odatda hech qachon tozalanmaydi.
Savol: Intervyuda "eng qiyin bug haqida gapirib bering" deyishganda, qanday javob kuchli hisoblanadi?
Javob: Kuchli javob STAR strukturasiga amal qiladi, lekin Action qismiga eng ko'p vaqt ajratadi: qanday qilib muammoni reproduce qildingiz, qaysi gipotezalarni ko'rib chiqdingiz va nima uchun ularni rad etdingiz, ildiz sababni qanday aniq topdingiz (log, debugger, kodni statik o'qish), va nega aynan shu yechimni (muqobillarga nisbatan) tanladingiz. Zaif javob faqat Situation va Result'ni aytadi ("murakkab bug bor edi, men tuzatdim, endi ishlayapti") — bu hech qanday texnik chuqurlikni ko'rsatmaydi.
Savol: Production monitoring (masalan Sentry/Crashlytics) bilan ishlashda qanday amaliy qoidaga amal qilish kerak, real misol asosida?
Javob: Xato monitoring alohida oqimlarga (masalan alohida modul/SDK uchun) ajratilishi foydali bo'lishi mumkin — shunda bitta modulning (masalan uchinchi tomon BLE SDK'sining) shovqinli xatolari asosiy ilova xatolarini loglarda ko'mib yubormaydi. Bundan tashqari, har qanday tarmoq so'rovi interceptor darajasida avtomatik xato hisobotini yuborishi kerak (status kod, javob tanasi, yo'l bilan birga) — shunda "nega saqlash ishlamadi" degan savolga birinchi javob har doim monitoring paneli bo'ladi, kodni qayta o'qishdan oldin.
Ko'p uchraydigan xato javoblar#
| Savol turi | Zaif (junior darajadagi) javob | Kuchli (senior darajadagi) javob |
|---|---|---|
| "Eng qiyin bug haqida gapiring" | "Bir marta chuqur bug bor edi, ko'p vaqt ketdi, lekin tuzatdim" | Aniq: qanday debug qilindi, qaysi gipotezalar rad etildi, ildiz sabab nima edi, nega aynan shu yechim tanlandi |
| "Concurrency muammosini qanday hal qilasiz?" | "Mutex/lock qo'yaman" | Avval operatsiyani atomik qilib qayta loyihalash mumkinmi, shundan keyingina qulflash mexanizmiga o'tish |
| "Xatoni qanday ifodalaysiz?" | "Xato bo'lsa -1/0 qaytaraman" | Xato holati domenning haqiqiy qiymati bilan hech qachon bir xil turda bo'lmasligi kerak — null/Result |
| "Gibrid (web+native) arxitektura qanday bo'lishi kerak?" | "Ikkalasida ham biznes-mantiq bo'lishi mumkin, qulayroq joyda" | Qaror qabul qilish bitta joyda (odatda deploy tezroq bo'lgan tomonda) jamlanishi kerak — aks holda drift |
| "Eskirgan kodni qachon o'chirasiz?" | "Vaqt topganimda tozalayman" | Feature eskirgan zahoti — "keyinroq" odatda "hech qachon" degani |
O'z production tajribangizni STAR formatida yozing
O'zingiz ishlagan (yoki o'rganish jarayonida duch kelgan) bitta texnik muammoni STAR (Situation, Task, Action, Result) formatida, 150-200 so'zda yozib chiqing. Action qismiga alohida e'tibor bering — aynan qaysi qadamlar orqali ildiz sababga yetib bordingiz? Yozib bo'lgach, uni ovoz chiqarib o'qing — agar u faqat "muammo bor edi, men tuzatdim" deb eshitilsa, Action qismini kengaytiring.
Maslahatni ko'rsatish
Agar hali jiddiy production bug bilan ishlamagan bo'lsangiz, o'zingizning pet-loyihangizdan yoki o'quv loyihasidan eng qiyin texnik muammoni tanlang — muhimi format, hajm emas.