RƏSƏDXANA

Təhqiqatlar

Dərslər nə düşünmək lazım olduğunu öyrədir; bunlar düşüncənin özünü göstərir. Hər təhqiqat eyni sıra ilə gedir, çünki o sıra metodun özüdür: şikayət, ilk təxmin (çox vaxt səhv, elə buna görə də faydalı), dəlil, səbəb, düzəliş, və xətanın yerində indi duran yoxlama.

Səkkizi də AI ilə qurulmuş real bir layihədə baş verib və hər rəqəm onu düzəldən commit-dən oxunub — səhv çıxan ilk diaqnoz da daxil olmaqla.

Modul 01 · Kontekst mühəndisliyi

Bir null, iki məna: hamı dəstəkçi göründü

Şikayət

Heç kim şikayət etmədi. Sayta baxarkən görünürdü ki, ödəniş etməmiş hesablar da dəstəkçi nişanını və yalnız dəstəkçilərə açıq avatarları görür.

İlk təxmin

İlk fikir: «yəqin kimsə səhvən hamıya dəstəkçi statusu verib, bazadan silmək lazımdır». Bu ağlabatan idi, çünki nişan doğrudan da görünürdü.

Təhqiqatı aç

Dəlil

  1. 1Bazaya baxıldı: entitlements cədvəli tamamilə boş idi. Deməli heç kimə heç nə verilməmişdi — problem məlumatda deyil, oxunuşda idi.
  2. 2Client kodu oxundu: dəstəkçi olub-olmamaq `validUntil === null || validUntil > indi` ifadəsi ilə hesablanırdı.
  3. 3Serverə baxıldı: /api/entitlement onsuz da açıq `active` bayrağı göndərirdi, /api/billing/status isə yalnız `validUntil` göndərirdi.

Səbəb

`validUntil = null` iki tamam fərqli halı bildirirdi: «ömürlük dəstəkçi» və «heç vaxt ödəməyib». Client birincisini nəzərdə tutub yazılmışdı, real həyatda isə ikincisi hökm sürürdü.

Düzəliş

Server hər iki endpoint-də açıq `active` bayrağı göndərir, client isə özü qərar vermir — bayrağı oxuyur.

Qoruyucu

Düzəliş canlı serverdə üç hal üçün yoxlanıldı: sətir yoxdur → active:false, valid_until NULL → active:true, vaxtı keçib → active:false.

Götürüləsi dərs: Şərtnamədə hər nullable sahə üçün «null nə deməkdir?» sualı verilsəydi, bu kod heç vaxt yazılmazdı. Qərarı client-ə buraxma — serverdən hökm gəlsin.

Bağlı dərslər:Əvvəl şərtnamə, sonra kiçik addımlar

Modul 02 · Git intizamı və təhlükəsizlik toru

«Sadəcə formatı düzəltdim» deyən redaktə

Şikayət

Bir neçə faylda mətn qəribə görünürdü. Build keçirdi, səhifələr açılırdı, test yox idi. Diff isə «kiçik təmizlik» kimi təqdim olunmuşdu.

İlk təxmin

İlk fikir: «yəqin encoding problemidir, fayl UTF-8 deyil». Məntiqli idi, çünki simvollar səhv görünürdü.

Təhqiqatı aç

Dəlil

  1. 1Fayl bayt səviyyəsində yoxlandı: grep faylı «binary» adlandırdı. Deməli içində mətn olmayan bayt var idi.
  2. 2Diff `--word-diff` ilə oxundu: dəyişən yeganə şey bir backslash idi.

Səbəb

Skriptlə edilən bir redaktə ikiqat backslash-ları tək backslash-a çevirmişdi. Adi sətirdə bu artıq escape ardıcıllığıdır: mətn idarəetmə simvoluna dönür və gözə görünmür.

Düzəliş

Zədələnmiş sətirlər əl ilə bərpa olundu və belə mətnlər üçün String.raw işlədildi.

Qoruyucu

Yoxlama skripti hər sətirdə xam idarəetmə simvolu axtarır və tapsa qırmızı olur. Bu kursun öz fayllarında da eyni yoxlama işləyir — və bir dəfə işə düşdü.

Götürüləsi dərs: Mexaniki görünən dəyişiklik davranışı dəyişə bilər. Diff-i xülasəyə görə yox, öz gözünlə oxu — xüsusən escape, encoding və generated fayllarda.

Bağlı dərslər:Git sənin geri al düyməndirYazmadığın, amma oxumalı olduğun əsaslar

Modul 03 · Kodu oxumaq və təhlükəsizlik auditi

Qapı yalnız interfeysdə idi

Şikayət

Şikayət yox idi. Xarici bir baxış «kimlik sorğunun gövdəsindən alınır» dedi və bu, yoxlanılası bir iddia idi.

İlk təxmin

Baxışın diaqnozu tam dəqiq deyildi: server gövdəyə hər halda inanmırdı, əvvəl sessiyaya baxırdı. Yəni iddia qismən yanlış idi — amma altında əsl problem vardı.

Təhqiqatı aç

Dəlil

  1. 1Hər endpoint üçün «kimlik haradan gəlir» cədvəli çıxarıldı. Səs vermə və üzv olma gövdədəki `voterKey` və `memberKey` dəyərlərini sessiya olmayanda qəbul edirdi.
  2. 2Sessiyasız curl göndərildi: post yazmaq, səs vermək və halqaya qoşulmaq işlədi. Deməli üzvlük qapısı yalnız UI-da idi.
  3. 3Giriş limitinin kodu oxundu: `record` yalnız uğurlu girişdən sonra çağırılırdı.

Səbəb

Üç ayrı boşluq, bir kökdən: server client-in özü haqqında dediklərinə inanırdı. Kimlik gövdədən gəlirdi, üzvlük yoxlanmırdı, limit isə səhv şeyi sayırdı.

Düzəliş

Yazma əməliyyatları Worker-də təsdiqlənmiş hesab tələb edir, kimlik yalnız sessiyadan gəlir, limit isə uğursuz cəhdləri sayır. Oxumaq bilərəkdən açıq qaldı.

Qoruyucu

Deploy edilmiş Worker-ə qarşı yoxlanıldı: sessiyasız post, səs və qoşulma 401 qaytarır, oxumaq isə 200. Aylar sonra eyni naxışın qonşu bir yerdə qaldığı tapıldı və o da bağlandı.

Götürüləsi dərs: İnterfeys kilid deyil. Hər endpoint üçün iki sual: kimlik haradan gəlir, icazə harada yoxlanılır. Cavabı curl ilə al, koddan oxuyub inanma.

Bağlı dərslər:İnterfeys kilid deyilSessiya, limitlər və sızmalar

Modul 04 · Arxitektura və tək həqiqət mənbəyi

Token-in iki sahibi, kimliyin iki nüsxəsi

Şikayət

«Ürək düyməsi işləmir» deyildi. Həm də: öz postunu silmək düyməsi görünmürdü və adını dəyişmək həmişə xəta verirdi.

İlk təxmin

İlk fikir: «səs yazılmır, endpoint qırıqdır». Yanlış idi — baza sətri yazılırdı. Səhv təxmin faydalı oldu: baxış səsin yazılmasından oxunmasına keçdi.

Təhqiqatı aç

Dəlil

  1. 1Baza yoxlandı: səs sətri var idi. Deməli yazma işləyir, oxuma yanılır.
  2. 2Səsin hansı açarla yazıldığı və hansı açarla oxunduğu müqayisə edildi: yazmaq sessiyanın hesab id-si ilə, oxumaq isə brauzerin göndərdiyi təsadüfi id ilə.
  3. 3Bu id-ni hesab id-si ilə əvəz etməli olan funksiya axtarıldı. Funksiya yazılmışdı, amma heç yerdən çağırılmırdı. Üstündəki şərh isə işin görüldüyünü deyirdi.
  4. 4Auth store oxundu: token bir açarla saxlanılır, API client isə başqa açardan oxuyurdu. Ona görə ad dəyişikliyi də 401 alırdı.

Səbəb

Bir faktın iki sahibi var idi — həm kimlik, həm də token. İki nüsxə heç vaxt üst-üstə düşmür, uyğunsuzluq isə xəta kimi yox, «işləmir» kimi görünür.

Düzəliş

Token bir modulun sahibliyinə verildi, kimliyi isə server təyin edir: səslər, sahiblik və silmək səlahiyyəti sessiyadan gəlir. Ölü funksiya qoşulmadı, silindi.

Qoruyucu

Açarlar bir qeydiyyat faylında toplandı, köhnə açar isə növbəti yüklənmədə yeni açara köçürülür ki, heç kim çıxış etməsin.

Götürüləsi dərs: «İşləmir» şikayəti gələndə soruş: bu fakt harada yazılır, harada oxunur? Ölü funksiya və onun doğru olduğunu iddia edən şərh — xətanın gizləndiyi yer.

Bağlı dərslər:Tək həqiqət mənbəyiKoddan əvvəl: xəritəni çək

Modul 05 · Dərin sistemlər və edge məhdudiyyətləri

10 millisaniyəlik büdcə, 410 millisaniyəlik hash

Şikayət

Xəta hələ baş verməmişdi. Ödənişli plandan pulsuz plana keçmək qərarı verilmişdi və sual belə idi: nə qırılacaq?

İlk təxmin

İlk fikir: «sayt yavaşlayacaq». Yanlış idi. Pulsuz plan eyni edge şəbəkəsində işləyir — işlər yavaşlamır, sadəcə dayandırılır.

Təhqiqatı aç

Dəlil

  1. 1Platformanın limitləri oxundu: sorğu başına 10ms CPU vaxtı, şəbəkə gözləməsi isə sayılmır.
  2. 2Ən ağır əməliyyat ölçüldü: bcrypt, cost 12 — 410ms. Limitdən qırx dəfə artıq.
  3. 3Alternativlər yoxlandı: platforma PBKDF2-ni 100 000 iterasiya ilə məhdudlaşdırır (təxminən 31ms), OWASP isə 600 000 tövsiyə edir. Yəni serverdə saxlamağa dəyən yavaş hash yox idi.

Səbəb

Kitabxana platformaya görə yox, ümumi məsləhətlərə görə seçilmişdi. Node-da normal olan seçim edge-də ölümcüldür.

Düzəliş

Yavaş açar törəməsi brauzerə köçdü: PBKDF2, 600 000 iterasiya. Server nəticənin sürətli SHA-256-sını saxlayır — qorunacaq aşağı entropiyalı sirr qalmadığı üçün bu düzgündür.

Qoruyucu

Heç kim parolunu sıfırlamadı: bcrypt hash-i duzunu öz içində daşıyır və duz sirr deyil, ona görə brauzer köhnə hash-i özü hesablayır. Offline sındırma müqaviməti isə artdı.

Götürüləsi dərs: Kitabxananı platformadan əvvəl seçmə. Ən ağır əməliyyatı ölç, limitlə müqayisə et, sonra qərar ver — və limit aşılırsa, işi sorğunun yolundan çıxar.

Bağlı dərslər:CPU büdcəsi: edge-də hər millisaniyə

Modul 06 · Domen modelləşdirmə və simulyasiyalar

Etiketlər üst-üstə düşür — və daha üç xəta

Şikayət

Şikayət kiçik idi: enerji səviyyələri diaqramında n=4, 5, 6 etiketləri bir-birinin üstünə düşür.

İlk təxmin

İlk fikir: «etiketləri bir az aralamaq lazımdır» — sırf yerləşdirmə məsələsi. Şikayətə baxmağa gedən adam isə modulun yarısının ölü olduğunu tapdı.

Təhqiqatı aç

Dəlil

  1. 1Ekranda göstərilən rəqəmlər oxundu: buraxılan enerji «−2.55 eV» yazırdı. Buraxılan foton üçün mənfi enerji mümkün deyil.
  2. 2Dalğa uzunluğu sahəsinə baxıldı: hər keçid üçün «—» göstərirdi, çünki hesablama müsbət enerji tələb edirdi.
  3. 3Diaqram tanış mənbələrlə müqayisə edildi: n=1 yuxarıda idi, yəni aşağı səviyyəyə «düşən» elektron ekranda yuxarı gedirdi.

Səbəb

Enerji E(aşağı) − E(yuxarı) kimi hesablanırdı, yəni işarə tərsinə idi. Bir işarə səhvi üç nəticə verdi: mənfi oxunuş, ölü dalğa uzunluğu, səhv rəng. Diaqramın tərs çəkilməsi isə ayrıca qərar idi.

Düzəliş

İşarə düzəldildi, əsas hal indi 2.55 eV və 486 nm göstərir — H-beta xətti — və keçid mavi-yaşıl çəkilir. Əsas vəziyyət aşağıya alındı.

Qoruyucu

Rəqəmlər bilinən dəyərlərlə müqayisə olunur. Etiket sıxlığı isə problem olmaqdan çıxdı: bütün xətlər qalır, yalnız etiketlər seyrəlir, çünki səviyyələrin sıxlaşması şəklin özüdür.

Götürüləsi dərs: Ekranda inandırıcı görünmək sübut deyil. Modeli bilinən cavabla müqayisə et. Kiçik şikayət çox vaxt böyük xətanın görünən ucudur.

Bağlı dərslər:Simulyasiya: riyaziyyatı AI-a düzgün demək

Modul 07 · Avtomatik sübut və statik analiz

47 «keçdi» yazan, amma heç bitməyən yoxlama

Şikayət

Yoxlamalar həmişə yaşıl idi. Şübhə isə başqa yerdən gəldi: bir xəta canlıda tapıldı, halbuki onu tutmalı olan yoxlama «keçdi» deyirdi.

İlk təxmin

İlk fikir: «yoxlama zəifdir, daha çox iddia yazmaq lazımdır». Əslində yoxlama yaxşı idi — sadəcə heç vaxt sona çatmırdı.

Təhqiqatı aç

Dəlil

  1. 1Yığıcı skriptin necə hökm verdiyinə baxıldı: çıxışdakı «ok» sətirlərini sayırdı.
  2. 2Suite qəsdən yarıda çökdürüldü: çap olunmuş «ok» sətirləri qaldı, sayğac yüksək çıxdı, uğursuzluq sayı sıfır oldu.

Səbəb

Ölçü saxtalaşdırıla bilən idi. Yarıda ölən proses də «ok» çap edir, ona görə sətir saymaq işin bitdiyini sübut etmir.

Düzəliş

Hökm exit code ilə verilir. Əlavə olaraq, sıfır iddia ilə bitən və demək olar heç nə yazmayan suite də uğursuz sayılır — boşaldılmış yoxlama da qoruma deyil.

Qoruyucu

Eyni yanaşma bu kursun yoxlamasında da var və bir dəfə işə düşdü: dörd browser suite-i «0 yoxlama» ilə bitmişdi, çünki dev server işləmirdi.

Götürüləsi dərs: Yoxlamanı da yoxlamaq lazımdır. Qaydanı qəsdən poz və qırmızı olduğunu gör; heç vaxt qırmızı olmayan yoxlama qoruma deyil, bəzəkdir.

Bağlı dərslər:«Hazırdır» sübut tələb edirLayihəni qoruyan yoxlamalar

Modul 08 · Canlı sistem, loglar və xərc

Lokalda girmiş, canlıda anonim

Şikayət

Lokalda hər şey işləyirdi. Canlıda isə adını və ya parolunu dəyişmək həmişə 401 verirdi, postlar isə hesaba bağlanmırdı.

İlk təxmin

İlk fikir: «sessiya bitir, token vaxtı qısa olmalıdır». Yanlış idi, çünki problem ilk saniyədən başlayırdı.

Təhqiqatı aç

Dəlil

  1. 1Brauzerin şəbəkə paneli açıldı: sorğularda Cookie header-i ümumiyyətlə yox idi.
  2. 2Domenlərə baxıldı: sayt Pages domenində, API isə workers.dev domenində idi — yəni hər sorğu cross-site.
  3. 3Cookie atributu oxundu: SameSite=Lax, yəni brauzer onu məhz bu halda göndərmir.

Səbəb

Lokalda hər şey bir domendə olduğu üçün fərq görünmürdü. Canlı mühit fərqli domenlər deməkdir və cookie qaydaları orada tamam başqa cür işləyir.

Düzəliş

Cookie `SameSite=None; Secure` oldu. Amma brauzerlər üçüncü tərəf cookie-lərini tədricən bağladığı üçün token həm də auth cavablarında qaytarılır və `Authorization: Bearer` ilə göndərilir. Server ikisini də qəbul edir.

Qoruyucu

Runbook-da lokal və canlı arasındakı fərqlər — domenlər, cookie atributları, limitlər — açıq yazılır, çünki bu fərqlər yalnız canlıda görünən xətaların mənbəyidir.

Götürüləsi dərs: «Mənim kompüterimdə işləyir» bir mühit haqqında məlumatdır, proqram haqqında yox. Canlının domenlərini, limitlərini və sirlərini əvvəlcədən yaz.

Bağlı dərslər:Deploy, sirlər, müşahidəSessiya, limitlər və sızmalar