aidoc.uz

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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
Situation
Qisqa kontekst: qanday tizim, nima kuzatilgan (masalan 'foydalanuvchilar yolg'on kritik-batareya bildirishnomasi olayotgan edi')
T
Task
Sizning vazifangiz aniq nima edi — ildiz sababni topish, yoki tuzatishni loyihalash
A
Action
Aniq texnik qadamlar: qanday debug qildingiz, nimani topdingiz, nima uchun aynan shu yechimni tanladingiz (muqobil variantlarni ham eslatish plyus)
R
Result
O'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 turiZaif (junior darajadagi) javobKuchli (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
Amaliy topshiriq

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.