# QA 13: Terminal, Git, Docker va Loglar — QA'ning muhandislik asboblari

> **Holat:** 2026-yil oktabr. Terminal va Git misollari macOS 26 (zsh) va Git 2.53 da ishga tushirib tekshirilgan — `git bisect`, `grep`, `curl` va `jq` chiqishlari haqiqiy. Docker Compose buyruqlari Docker'ning rasmiy hujjatlari asosida keltirilgan.

> **Bu darsdan keyin siz:** dasturchi bilan bir tilda gaplashadigan "texnik QA"ga aylanasiz.

- terminalda fayl, log va API bilan tez ishlaysiz (`grep`, `tail -f`, `curl`, `jq`);
- feature branch'ni lokal ishga tushirib test qilasiz va PR'ni QA ko'zi bilan o'qiysiz;
- `git bisect` bilan regressiyani keltirib chiqargan commit'ni bir necha qadamda topasiz;
- Docker Compose bilan lokal muhitni ko'tarasiz, loglarini o'qiysiz va tozalaysiz;
- log, request ID va stack trace'dan bug report uchun dalil yig'asiz.

## Mundarija

1. [Nega QA'ga terminal kerak](#nega-terminal)
2. [Terminal asoslari: navigatsiya va fayllar](#terminal-asoslari)
3. [grep, pipe va yo'naltirish: loglarda qidirish](#grep-pipe)
4. [curl va jq: API bilan terminaldan ishlash](#curl-jq)
5. [O'zgaruvchilar va skriptlar: mini smoke test](#skriptlar)
6. [Git QA uchun: branch, log, diff, blame, stash](#git)
7. [git bisect: regressiyani topish](#git-bisect)
8. [Muhitlar, feature flag'lar va .env](#muhitlar)
9. [Docker va Docker Compose: lokal muhit](#docker)
10. [Loglar: darajalar, structured log, request ID](#loglar)
11. [Stack trace va mobil loglar](#stack-trace)
12. [Amaliy mashqlar](#mashqlar)
13. [Xulosa](#xulosa)
14. [Manbalar](#manbalar)

---

## 1. Nega QA'ga terminal kerak {#nega-terminal}

Ko'p yangi QA'lar terminaldan qo'rqadi: qora ekran, hech qanday tugma yo'q. Lekin amalda u — eng tez "pult". Bir qator buyruq bilan 10 000 qatorlik logdan kerakli xatoni topasiz, API'ni brauzersiz chaqirasiz yoki dasturchining branch'ini o'z kompyuteringizda ishga tushirasiz.

| Vaziyat                                              | Terminalsiz                        | Terminal bilan                                     |
| ---------------------------------------------------- | ---------------------------------- | -------------------------------------------------- |
| Server logida bitta so'rov izini topish              | Faylni ochib, ko'z bilan qidirish  | `grep "req-7f3a" api.log`                          |
| API javob beryaptimi                                 | Postman ochish, so'rov yasash      | `curl -s -o /dev/null -w "%{http_code}" URL`       |
| Dasturchi "fix qildim" dedi — lekin qaysi branch'da? | Kutish, so'rash                    | `git switch feature/...` yoki `gh pr checkout 123` |
| "Qachondan buzildi?"                                 | Taxmin                             | `git bisect` bilan aniq commit                     |
| Lokal muhitni noldan ko'tarish                       | Ko'rsatma bo'yicha soatlab sozlash | `docker compose up -d`                             |

Bu dars dasturchi bo'lishni talab qilmaydi. Maqsad — QA ishida eng ko'p uchraydigan 20–30 ta buyruqni ishonch bilan ishlatish.

> **🎯 Intervyuda:** "Log fayldan ma'lum foydalanuvchi so'rovlarini qanday topasiz?" — `grep` bilan request ID yoki user ID bo'yicha qidiraman (`grep -n "req-7f3a" api.log`), kontekst uchun `-C 2`, real vaqtda kuzatish uchun `tail -f api.log | grep ERROR`. Structured (JSON) log bo'lsa — `jq 'select(.requestId == "req-7f3a")'`.

---

## 2. Terminal asoslari: navigatsiya va fayllar {#terminal-asoslari}

**Qayerda ishlaymiz:**

| OS      | Terminal                 | Shell                                                      |
| ------- | ------------------------ | ---------------------------------------------------------- |
| macOS   | Terminal.app yoki iTerm2 | `zsh` (macOS 10.15 dan beri default; undan oldin `bash`)   |
| Linux   | Istalgan terminal        | Ko'pincha `bash`                                           |
| Windows | Windows Terminal         | PowerShell; Linux buyruqlari uchun **WSL** tavsiya etiladi |

Bu darsdagi buyruqlar macOS, Linux va WSL'da ishlaydi. Windows PowerShell'da sintaksis farq qiladi.

### Eng kerakli buyruqlar

| Buyruq               | Nima qiladi                                                         | Misol                                                       |
| -------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------- |
| `pwd`                | Hozir qaysi papkadaman                                              | `pwd`                                                       |
| `ls -la`             | Papka ichidagilar (yashirin fayllar bilan)                          | `ls -la logs/`                                              |
| `cd`                 | Papkaga o'tish                                                      | `cd ~/projects/demobozor`, `cd ..`                          |
| `mkdir -p`           | Papka yaratish (ichma-ich ham)                                      | `mkdir -p evidence/BUG-1234`                                |
| `cp`, `mv`           | Nusxa olish, ko'chirish/qayta nomlash                               | `cp api.log api.backup.log`                                 |
| `cat`                | Faylni to'liq chiqarish                                             | `cat version.json`                                          |
| `less`               | Katta faylni varaqlab o'qish (`/` qidirish, `q` chiqish)            | `less api.log`                                              |
| `head -n`, `tail -n` | Boshidan / oxiridan N qator                                         | `tail -n 50 api.log`                                        |
| `tail -f`            | Faylga yozilayotganlarni real vaqtda kuzatish (`Ctrl+C` to'xtatadi) | `tail -f api.log`                                           |
| `wc -l`              | Qatorlar soni                                                       | `wc -l api.log`                                             |
| `find`               | Fayl qidirish                                                       | `find . -name "*.log" -mtime -1` (oxirgi 1 kunda o'zgargan) |

Vaqtni tejaydigan odatlar: `Tab` — nomni avtomatik to'ldiradi; `↑` — oldingi buyruq; `Ctrl+R` — tarixdan qidirish; `Ctrl+C` — ishlayotgan buyruqni to'xtatish; `clear` — ekranni tozalash.

> **⚠️ Diqqat:** `rm` faylni **Savatga emas, butunlay** o'chiradi, `rm -rf papka` esa butun papkani so'roqsiz yo'q qiladi. Buyruqni boshqa joydan ko'chirib olganda, `rm` bilan boshlansa — Enter bosishdan oldin ikki marta o'qing.

---

## 3. grep, pipe va yo'naltirish: loglarda qidirish {#grep-pipe}

Mashq uchun DemoBozor API'ning (xayoliy) log fayli. Quyidagi buyruq faylni yaratadi — `<<'EOF'` dan `EOF` gacha bo'lgan matn faylga yoziladi:

```bash
cat > api.log <<'EOF'
2026-10-08T16:25:01.112Z INFO  [req-5a1c] POST /api/v1/orders user=9 status=201 ms=184
2026-10-08T16:25:03.540Z INFO  [req-5a1d] GET /api/v1/products?query=choynak status=200 ms=42
2026-10-08T16:26:00.020Z WARN  [req-7f3a] POST /api/v1/payments order=11 provider=UZCARD retry=1
2026-10-08T16:26:00.871Z ERROR [req-7f3a] POST /api/v1/payments order=11 status=502 ms=850 msg="provider timeout"
2026-10-08T16:26:05.300Z INFO  [req-5a1e] GET /api/v1/cart user=3 status=200 ms=37
2026-10-08T16:28:30.004Z INFO  [req-7f3b] POST /api/v1/payments order=11 provider=UZCARD status=201 ms=410
2026-10-08T16:28:30.010Z ERROR [req-7f3b] amount mismatch order=11 expected=135000 paid=130000
2026-10-08T16:30:12.777Z INFO  [req-5a1f] GET /api/v1/orders/11 user=9 status=200 ms=29
2026-10-08T16:31:40.101Z WARN  [req-5a20] POST /api/v1/auth/verify phone=+99890***4567 attempts=3 status=429
2026-10-08T16:31:41.500Z INFO  [req-5a21] GET /api/v1/products?query=gilam status=200 ms=51
EOF
```

### grep — matn bo'yicha qidirish

```bash
$ grep -n "ERROR" api.log
4:2026-10-08T16:26:00.871Z ERROR [req-7f3a] POST /api/v1/payments order=11 status=502 ms=850 msg="provider timeout"
7:2026-10-08T16:28:30.010Z ERROR [req-7f3b] amount mismatch order=11 expected=135000 paid=130000

$ grep -c "ERROR" api.log
2

$ grep "req-7f3a" api.log
2026-10-08T16:26:00.020Z WARN  [req-7f3a] POST /api/v1/payments order=11 provider=UZCARD retry=1
2026-10-08T16:26:00.871Z ERROR [req-7f3a] POST /api/v1/payments order=11 status=502 ms=850 msg="provider timeout"

$ grep -E "status=5[0-9]{2}" api.log
2026-10-08T16:26:00.871Z ERROR [req-7f3a] POST /api/v1/payments order=11 status=502 ms=850 msg="provider timeout"
```

| Flag               | Ma'nosi                                     | Qachon kerak                             |
| ------------------ | ------------------------------------------- | ---------------------------------------- |
| `-n`               | Qator raqamini ko'rsatadi                   | Bug report'da aniq joyga ishora qilish   |
| `-c`               | Faqat sonini chiqaradi                      | "Nechta xato bor?"                       |
| `-i`               | Katta-kichik harfni farqlamaydi             | `error`, `Error`, `ERROR` hammasi        |
| `-v`               | Teskari: mos **kelmagan** qatorlar          | `grep -v INFO` — shovqinni olib tashlash |
| `-E`               | Kengaytirilgan regex                        | `status=5[0-9]{2}` — barcha 5xx          |
| `-o`               | Faqat mos kelgan qismni chiqaradi           | Statistika yig'ish                       |
| `-C 2`, `-A`, `-B` | Atrofdagi qatorlar (context, after, before) | Xatodan oldin nima bo'ldi?               |
| `-r`               | Papka ichida rekursiv                       | `grep -rn "FREE_DELIVERY" src/`          |
| `-w`               | Butun so'z                                  | `-w 200` — `2001` ga mos kelmaydi        |

### Pipe (`|`) — buyruqlarni zanjir qilish

Pipe bir buyruq chiqishini keyingisiga kirish qilib beradi. Status kodlar statistikasi bir qatorda:

```bash
$ grep -o "status=[0-9]*" api.log | sort | uniq -c | sort -rn
   4 status=200
   2 status=201
   1 status=502
   1 status=429

$ grep -v "INFO" api.log | wc -l
       4
```

O'qish tartibi: status'larni ajratib ol → saralab qo'y → bir xillarini sanab chiqar → sanoq bo'yicha kamayish tartibida sarala.

### Yo'naltirish: dalilni faylga saqlash

```bash
grep "req-7f3a" api.log > evidence-BUG-1234.txt    # > — fayl yaratadi yoki USTIDAN yozadi
grep "req-7f3b" api.log >> evidence-BUG-1234.txt   # >> — oxiriga qo'shadi
some-command 2>&1 | tee run.log                    # xatolarni ham ushlab, ekranga va faylga
```

> **⚠️ Diqqat:** `>` mavjud faylni so'roqsiz qayta yozadi. Muhim logni `>` bilan "ustidan" yozib yubormang — qo'shish uchun `>>`.

### Real vaqtda kuzatish

```bash
tail -f api.log | grep --line-buffered "ERROR"
```

Ilovada amal bajarasiz — terminalda xato darhol paydo bo'ladi. `Ctrl+C` bilan to'xtatiladi. Bu "qaysi bosish qaysi xatoni keltirib chiqardi" degan savolga eng tez javob.

### macOS (BSD) va Linux (GNU) farqlari

macOS'dagi ko'p buyruqlar BSD versiyasi, Linux'da esa GNU. Internetdagi misollar ko'pincha GNU uchun yozilgan:

| Vazifa                   | Linux (GNU)             | macOS (BSD)                                              |
| ------------------------ | ----------------------- | -------------------------------------------------------- |
| Perl regex               | `grep -P "\d+"`         | `/usr/bin/grep` `-P`'ni qo'llamaydi → `grep -E "[0-9]+"` |
| Faylni joyida tahrirlash | `sed -i 's/a/b/' f.txt` | `sed -i '' 's/a/b/' f.txt`                               |
| Kechagi sana             | `date -d yesterday +%F` | `date -v-1d +%F`                                         |
| Versiyani ko'rish        | `sed --version`         | Ko'p BSD vositalarida `--version` yo'q                   |

Biror buyruq macOS'da "illegal option" desa — birinchi gumon shu farq.

---

## 4. curl va jq: API bilan terminaldan ishlash {#curl-jq}

`curl` — HTTP so'rov yuboradi, `jq` — JSON javobni filtrlaydi va chiroyli ko'rsatadi. `jq` odatda alohida o'rnatiladi: macOS'da `brew install jq`, Debian/Ubuntu'da `sudo apt-get install jq`, Windows'da `winget install jqlang.jq`.

```bash
$ curl -s https://jsonplaceholder.typicode.com/todos/1 | jq .
{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

$ curl -s -o /dev/null -w "%{http_code}\n" https://restful-booker.herokuapp.com/ping
201

$ curl -s -X POST https://restful-booker.herokuapp.com/auth \
    -H "Content-Type: application/json" \
    -d '{"username":"admin","password":"password123"}' | jq -r .token
e8dcaccbe7170f4

$ curl -s https://jsonplaceholder.typicode.com/users | jq -r '.[] | "\(.id)\t\(.email)"' | head -3
1	Sincere@april.biz
2	Shanna@melissa.tv
3	Nathan@yesenia.net
```

Token har safar boshqacha bo'ladi — sizdagi qiymat farq qiladi.

| `curl` flag                      | Ma'nosi                                      |
| -------------------------------- | -------------------------------------------- |
| `-s`                             | Jim rejim (progress chiqmaydi)               |
| `-i`                             | Javob header'larini ham ko'rsatadi           |
| `-X POST`                        | HTTP metod                                   |
| `-H "..."`                       | Header qo'shish                              |
| `-d '...'`                       | So'rov tanasi (body)                         |
| `-o /dev/null -w "%{http_code}"` | Tanani tashlab, faqat status kodni chiqarish |
| `--max-time 10`                  | 10 soniyadan oshsa to'xtatish                |

| `jq` ifoda                  | Ma'nosi                                  |
| --------------------------- | ---------------------------------------- |
| `.`                         | Butun JSON, chiroyli formatda            |
| `.title`, `.data.id`        | Maydon qiymati                           |
| `-r`                        | Qo'shtirnoqsiz "xom" matn                |
| `length`                    | Massiv uzunligi / obyekt maydonlari soni |
| `.[]`                       | Massiv elementlari bo'ylab               |
| `select(.level == "error")` | Shartga mos elementlar                   |

### QA topilmasi: "200 OK" ichidagi xato

```bash
$ curl -s -o /dev/null -w "%{http_code}\n" -X POST https://restful-booker.herokuapp.com/auth \
    -H "Content-Type: application/json" -d '{"username":"admin","password":"xato"}'
200
$ curl -s -X POST https://restful-booker.herokuapp.com/auth \
    -H "Content-Type: application/json" -d '{"username":"admin","password":"xato"}'
{"reason":"Bad credentials"}
```

Noto'g'ri parol — lekin status **200**. REST odatlariga ko'ra bu `401 Unauthorized` bo'lishi kerak edi. Faqat status kodga qaraydigan avtotest bu holatni "muvaffaqiyatli login" deb o'tkazib yuboradi. Restful-booker o'quv maqsadida atayin "xato" qilingan API — real loyihada bu bug report bo'lardi ([QA 11](/ios-lessons/qa-api-advanced)).

> **💡 Maslahat:** Chrome DevTools'dagi va Proxyman'dagi **Copy as cURL** funksiyasi brauzer yoki mobil ilova yuborgan so'rovni aynan terminal buyrug'i ko'rinishida beradi ([QA 9](/ios-lessons/qa-web-http-devtools), [QA 15](/ios-lessons/qa-proxyman)). Uni bug report'ga qo'shsangiz, dasturchi muammoni bir buyruq bilan takrorlaydi. Faqat ichidagi token va shaxsiy ma'lumotlarni yashirishni unutmang.

---

## 5. O'zgaruvchilar va skriptlar: mini smoke test {#skriptlar}

**Environment variable** — buyruqlar va dasturlar o'qiydigan nomlangan qiymat:

```bash
export BASE_URL=https://staging.demobozor.example   # joriy terminal sessiyasi uchun
echo $BASE_URL
env | grep BASE_URL
BASE_URL=http://localhost:3001 ./smoke.sh          # faqat shu bitta buyruq uchun
```

Endi takrorlanadigan tekshiruvni skriptga aylantiramiz. `smoke.sh` — muhim endpoint'lar kutilgan status kodni qaytarayotganini tekshiradi:

```bash
#!/usr/bin/env bash
# smoke.sh — muhim endpoint'lar javob beryaptimi? (QA uchun mini smoke)
set -u
BASE_URL="${BASE_URL:-https://restful-booker.herokuapp.com}"
failed=0

check() {
  local path="$1" expected="$2"
  local code
  code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$BASE_URL$path")
  if [ "$code" = "$expected" ]; then
    echo "✅ $path → $code"
  else
    echo "❌ $path → $code (kutilgan: $expected)"
    failed=$((failed + 1))
  fi
}

check "/ping" 201
check "/booking" 200
check "/booking/0" 404

if [ "$failed" -gt 0 ]; then
  echo "Smoke FAILED: $failed ta tekshiruv yiqildi"
  exit 1
fi
echo "Smoke PASSED"
```

```bash
$ chmod +x smoke.sh
$ ./smoke.sh
✅ /ping → 201
✅ /booking → 200
✅ /booking/0 → 404
Smoke PASSED
$ echo $?
0

$ BASE_URL=https://jsonplaceholder.typicode.com ./smoke.sh
❌ /ping → 404 (kutilgan: 201)
❌ /booking → 404 (kutilgan: 200)
✅ /booking/0 → 404
Smoke FAILED: 2 ta tekshiruv yiqildi
$ echo $?
1
```

Muhim tafsilotlar:

- `#!/usr/bin/env bash` (shebang) — faylni qaysi dastur bajarishini aytadi; `chmod +x` uni ishga tushiriladigan qiladi.
- `${BASE_URL:-...}` — o'zgaruvchi berilmasa, default qiymat.
- **Exit code**: `0` — muvaffaqiyat, `0` dan boshqasi — xato. `$?` oxirgi buyruqning exit code'ini ko'rsatadi. CI tizimlari aynan shunga qarab pipeline'ni "qizil" yoki "yashil" qiladi ([QA 21](/ios-lessons/qa-ci-cd)).

---

## 6. Git QA uchun: branch, log, diff, blame, stash {#git}

Git — kod tarixini saqlaydigan tizim. QA uchun u "vaqt mashinasi": istalgan versiyani ishga tushirish, nima o'zgarganini ko'rish, xato qachon paydo bo'lganini topish. To'liq asoslar: [Git for iOS Developers](/ios-lessons/git) va [Git buyruqlari cheat sheet](/ios-lessons/git-commands).

| Tushuncha                         | QA uchun ma'nosi                                              |
| --------------------------------- | ------------------------------------------------------------- |
| Repository (repo)                 | Loyiha va uning butun tarixi                                  |
| Commit                            | Bitta saqlangan o'zgarish (hash, muallif, sana, xabar)        |
| Branch                            | Parallel ish chizig'i: `main`, `feature/promo-code-v2`        |
| Pull Request (PR) / Merge Request | "Mening branch'imni `main`'ga qo'shing" degan so'rov + review |
| Tag                               | Reliz belgisi: `v1.2`, `v1.3`                                 |

### Dasturchining branch'ini test qilish

```bash
git clone https://github.com/<org>/demobozor.git   # bir marta
cd demobozor
git fetch origin                                    # yangi branch'larni olib kelish
git switch feature/promo-code-v2                    # branch'ga o'tish
git pull                                            # eng so'nggi commit'lar
gh pr checkout 123                                  # GitHub CLI: PR raqami bo'yicha (gh o'rnatilgan bo'lsa)
```

### Nima o'zgardi? Reliz orasidagi farq

Regression testing ko'lamini tanlashning eng aniq usuli — ikki reliz orasidagi commit'lar va fayllarga qarash. Quyidagi chiqishlar 7-bo'limdagi mashq repo'sidan:

```bash
$ git log --oneline v1.2..v1.3
d0634b0 versiya 1.3.0
5532ddd README yangilandi
1257f6b magic number'lar konstantaga chiqarildi
4bfe1f8 viloyatlar uchun yetkazish muddati

$ git diff --stat v1.2 v1.3
 README.md    | 1 +
 delivery.mjs | 9 ++++++++-
 version.json | 1 +
 3 files changed, 10 insertions(+), 1 deletion(-)
```

`delivery.mjs` o'zgargan — demak yetkazish narxi va muddati regression ro'yxatida birinchi o'rinda. "Refactor" so'zi aldamasin: "faqat kodni chiroyli qildim" degan commit'lar ham xatti-harakatni o'zgartirib yuborishi mumkin.

### Kim, qachon, nima uchun: blame va pickaxe

```bash
$ git blame -L 4,7 delivery.mjs
^6aa5588 (QA Demo 2026-10-09 22:14:44 +0500 4) export function deliveryFee(subtotal, isPremium = false) {
^6aa5588 (QA Demo 2026-10-09 22:14:44 +0500 5)   if (isPremium) return 0;
1257f6b2 (QA Demo 2026-10-09 22:14:44 +0500 6)   return subtotal > FREE_DELIVERY_THRESHOLD ? 0 : DELIVERY_FEE;
^6aa5588 (QA Demo 2026-10-09 22:14:44 +0500 7) }

$ git log -S "FREE_DELIVERY_THRESHOLD" --oneline
1257f6b magic number'lar konstantaga chiqarildi
```

`git blame` har bir qatorni oxirgi o'zgartirgan commit'ni ko'rsatadi. `git log -S "matn"` esa shu matn qo'shilgan yoki olib tashlangan commit'larni topadi. Maqsad aybdor qidirish emas — **kontekst**: o'sha commit'ning PR'i va tiketini o'qib, o'zgarish sababini bilib olasiz.

### Yarim ishni vaqtincha chetga qo'yish: stash

```bash
$ git stash -u          # o'zgarishlar (yangi fayllar bilan) vaqtincha saqlanadi
Saved working directory and index state WIP on feature/qa-test: 0cd74b8 versiya 1.3.0
$ git stash list
stash@{0}: WIP on feature/qa-test: 0cd74b8 versiya 1.3.0
$ git switch main       # toza holatda boshqa branch'ga o'tasiz
$ git stash pop         # qaytib kelib, saqlanganini tiklaysiz
```

### PR'ni QA ko'zi bilan o'qish

| Nimaga qarash                       | Nega                                               |
| ----------------------------------- | -------------------------------------------------- |
| Bog'langan tiket va qabul mezonlari | Nimani test qilish kerakligi shu yerda             |
| O'zgargan fayllar ro'yxati          | Risk zonalari va regression ko'lami                |
| Testlar qo'shilganmi / o'zgarganmi  | Avtomat qamrov bormi                               |
| Migratsiyalar (DB sxema)            | Ma'lumotga ta'sir, rollback xavfi                  |
| Konfiguratsiya, feature flag'lar    | Ikkala holatni ham test qilish kerak               |
| CI natijasi                         | Qizil CI bilan qo'lda test boshlamang              |
| Skrinshot / video                   | UI o'zgarishini kutilgan dizayn bilan solishtirish |

> **⚠️ Diqqat:** `git reset --hard`, `git clean -fd`, `git checkout -- .` saqlanmagan o'zgarishlarni **qaytarib bo'lmas** tarzda o'chiradi. QA sifatida ular deyarli kerak bo'lmaydi; ishlatishdan oldin `git status` bilan nima yo'qolishini ko'ring yoki `git stash` qiling.

---

## 7. git bisect: regressiyani topish {#git-bisect}

**Ssenariy.** DemoBozor v1.2 da 300 000 so'mlik buyurtmaga yetkazish bepul edi. v1.3 da xaridorlar 20 000 so'm to'layapti. Ikki reliz orasida o'nlab commit bo'lishi mumkin — qaysi biri buzdi?

`git bisect` **binary search** qiladi: "yaxshi" va "yomon" commit orasini har safar teng ikkiga bo'ladi. 1000 ta commit orasidan aybdorni taxminan 10 ta tekshiruvda topadi.

![Git oqimi va QA nuqtalari: branch, PR, CI, staging, merge, reliz va bisect](/lessons/images/qa/qa_13_git_flow_qa.svg)

### Mashq repo'sini yaratish

Quyidagi skript 6 ta commit'li kichik repo yaratadi (sizdagi commit hash'lari boshqacha bo'ladi — bu normal):

```bash
#!/usr/bin/env bash
# bisect-demo.sh — git bisect mashqi uchun kichik repo yaratadi
set -euo pipefail
mkdir delivery-demo && cd delivery-demo
git init -q -b main
git config user.name "QA Demo"
git config user.email "qa-demo@example.com"

fee_v1='export function deliveryFee(subtotal, isPremium = false) {
  if (isPremium) return 0;
  return subtotal >= 300000 ? 0 : 20000;
}'
fee_v2='const FREE_DELIVERY_THRESHOLD = 300000;
const DELIVERY_FEE = 20000;

export function deliveryFee(subtotal, isPremium = false) {
  if (isPremium) return 0;
  return subtotal > FREE_DELIVERY_THRESHOLD ? 0 : DELIVERY_FEE;
}'
days='export function deliveryDays(region) {
  return region === "Toshkent" ? 1 : 4;
}'

commit() { git add -A && git commit -q -m "$1"; }

echo "$fee_v1" > delivery.mjs;                         commit "premium mijozlar uchun bepul yetkazish"
echo "# DemoBozor delivery" > README.md;               commit "README qo'shildi"; git tag v1.2
printf '%s\n\n%s\n' "$fee_v1" "$days" > delivery.mjs; commit "viloyatlar uchun yetkazish muddati"
printf '%s\n\n%s\n' "$fee_v2" "$days" > delivery.mjs; commit "magic number'lar konstantaga chiqarildi"
echo "Narxlar so'mda." >> README.md;                   commit "README yangilandi"
echo '{ "version": "1.3.0" }' > version.json;          commit "versiya 1.3.0"; git tag v1.3
git log --oneline --decorate
```

Va QA tekshiruvi — repo'dan **tashqarida** turadigan `qa-check.mjs` (Node.js kerak):

```javascript
// QA tekshiruvi: 300 000 so'mlik buyurtmada yetkazish bepul bo'lishi kerak
import { pathToFileURL } from "node:url";

const { deliveryFee } = await import(
  pathToFileURL(`${process.cwd()}/delivery.mjs`).href
);
const fee = deliveryFee(300000);
console.log(`deliveryFee(300000) = ${fee}`);
process.exit(fee === 0 ? 0 : 1); // 0 = good, 1 = bad
```

```bash
bash bisect-demo.sh        # repo yaratiladi
cd delivery-demo
```

### Qo'lda bisect

```bash
$ git bisect start
status: waiting for both good and bad commits
$ git bisect bad                 # hozirgi HEAD (v1.3) — buzuq
status: waiting for good commit(s), bad commit known
$ git bisect good v1.2           # v1.2 — ishlagan
Bisecting: 1 revision left to test after this (roughly 1 step)
[1257f6b2802163502533eb65a89c3a40712cf956] magic number'lar konstantaga chiqarildi
$ node ../qa-check.mjs
deliveryFee(300000) = 20000
$ git bisect bad
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[4bfe1f8f9989f70abf85a147ddd6afddb727bb6c] viloyatlar uchun yetkazish muddati
$ node ../qa-check.mjs
deliveryFee(300000) = 0
$ git bisect good
1257f6b2802163502533eb65a89c3a40712cf956 is the first bad commit
commit 1257f6b2802163502533eb65a89c3a40712cf956
Author: QA Demo <qa-demo@example.com>

    magic number'lar konstantaga chiqarildi

 delivery.mjs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
$ git bisect reset               # bisect tugadi, main'ga qaytamiz
```

Har qadamda git sizga bitta commit'ni "checkout" qiladi; siz test qilib `good` yoki `bad` deysiz. Real loyihada "test" — ilovani ishga tushirib, qo'lda tekshirish ham bo'lishi mumkin.

### Avtomatik bisect: `git bisect run`

Tekshiruv skript bo'lsa, butun jarayonni git'ning o'zi bajaradi:

```bash
$ git bisect start v1.3 v1.2     # avval "bad", keyin "good"
$ git bisect run node ../qa-check.mjs
running 'node' '../qa-check.mjs'
deliveryFee(300000) = 20000
Bisecting: 0 revisions left to test after this (roughly 0 steps)
running 'node' '../qa-check.mjs'
deliveryFee(300000) = 0
1257f6b2802163502533eb65a89c3a40712cf956 is the first bad commit
...
bisect found first bad commit
$ git bisect reset
```

Skriptning exit code'i git uchun hukm:

| Exit code                    | Ma'nosi                                                                            |
| ---------------------------- | ---------------------------------------------------------------------------------- |
| `0`                          | good — bu commit'da xato yo'q                                                      |
| `1`–`127` (125 dan tashqari) | bad — xato bor                                                                     |
| `125`                        | bu commit'ni test qilib bo'lmaydi (masalan, build bo'lmaydi) — o'tkazib yuboriladi |
| boshqalar                    | bisect to'xtatiladi                                                                |

Aybdor commit'ning diff'i hamma narsani aytadi:

```text
-  return subtotal >= 300000 ? 0 : 20000;
+  return subtotal > FREE_DELIVERY_THRESHOLD ? 0 : DELIVERY_FEE;
```

"Faqat magic number'larni konstantaga chiqarish" vaqtida `>=` jimgina `>` ga aylangan. Bug report'ga "Regression v1.3: `1257f6b` commit'da kiritilgan, `deliveryFee` ichida `>=` → `>`" deb yozsangiz, dasturchi tuzatishni minutlarda qiladi. Shu xato SQL darsida ma'lumotlar bazasida ham ko'ringan edi ([QA 12](/ios-lessons/qa-sql)) — 4-buyurtma.

> **🎯 Intervyuda:** "Regressiya qaysi commit'da paydo bo'lganini qanday topasiz?" — Ishlagan versiya (masalan, oldingi reliz tag'i) va buzilgan versiyani aniqlab, `git bisect` bilan binary search qilaman; tekshiruvni skriptga aylantira olsam — `git bisect run`. Natijada aybdor commit, uning PR'i va tiketini bug report'ga qo'shaman.

---

## 8. Muhitlar, feature flag'lar va .env {#muhitlar}

Bitta ilova bir nechta **muhit**da (environment) yashaydi:

| Muhit          | Kim ishlatadi                  | Ma'lumot                          | QA nima qiladi                            |
| -------------- | ------------------------------ | --------------------------------- | ----------------------------------------- |
| Local          | Dasturchi, QA o'z kompyuterida | Sun'iy, istalgancha buzish mumkin | Feature branch'ni tekshirish, eksperiment |
| Dev            | Jamoa, har merge'dan keyin     | Sun'iy                            | Tez tekshiruv, integratsiya               |
| Staging / QA   | QA jamoasi                     | Prod'ga yaqin, anonimlashtirilgan | Asosiy testlash, regression               |
| Pre-prod / UAT | Biznes, mijoz                  | Prod nusxasi                      | Qabul testlari (acceptance)               |
| Production     | Haqiqiy foydalanuvchilar       | Haqiqiy                           | Smoke, monitoring, faqat o'qish           |

### Feature flag'lar

**Feature flag** — funksiyani kodni qayta deploy qilmasdan yoqib-o'chirish imkoniyati. Masalan, `promo_v2_enabled`. QA uchun qoida: flag bilan yopilgan funksiyani **ikkala holatda ham** test qiling — yoqilganda ishlashi va o'chirilganda eski oqim buzilmagani. Bir nechta flag bo'lsa, ularning kombinatsiyalari ham risk.

### Konfiguratsiya va `.env`

Ilova sozlamalarini (API manzili, kalitlar, log darajasi) koddan tashqarida, environment variable'larda saqlash — keng tarqalgan amaliyot ("The Twelve-Factor App" metodologiyasi). Lokal ishlab chiqishda ular odatda `.env` faylda turadi:

```bash
# .env.example — repo'da saqlanadi, haqiqiy qiymatlarsiz
BASE_URL=http://localhost:3001
API_BASE_URL=http://localhost:3001/api/v1
LOG_LEVEL=debug
SMS_PROVIDER=mock
PAYMENT_PROVIDER_KEY=
```

> **⚠️ Diqqat:** Haqiqiy `.env` (parollar, token'lar, API kalitlar) **hech qachon** Git'ga commit qilinmaydi — u `.gitignore`'da bo'lishi kerak. Repo'da faqat bo'sh qiymatli `.env.example` turadi. PR'da `.env` yoki kalit ko'rinib qolsa — bu darhol xabar qilinadigan xavfsizlik muammosi.

---

## 9. Docker va Docker Compose: lokal muhit {#docker}

"Mening kompyuterimda ishlaydi" muammosining yechimi — **Docker**: ilova va uning bog'liqliklari (baza, kesh, navbat) bir xil "konteyner"larda ishlaydi.

| Tushuncha      | Ma'nosi                                                                 |
| -------------- | ----------------------------------------------------------------------- |
| Image          | Ilovaning tayyor "qolipi" (masalan, `postgres:17`)                      |
| Container      | Image'dan ishga tushirilgan nusxa                                       |
| Volume         | Konteyner o'chirilsa ham saqlanadigan ma'lumot (masalan, baza fayllari) |
| Docker Compose | Bir nechta konteynerni bitta fayl bilan boshqarish                      |

Docker'ni o'zingiz o'rnatasiz: macOS va Windows uchun **Docker Desktop**, Linux uchun Docker Engine (rasmiy hujjatga qarang). Compose faylining tavsiya etilgan nomi — `compose.yaml` (eski `docker-compose.yml` ham qo'llab-quvvatlanadi).

DemoBozor'ning lokal muhiti uchun o'quv misoli:

```yaml
# compose.yaml — DemoBozor lokal muhiti (o'quv misoli)
services:
  db:
    image: postgres:17
    environment:
      POSTGRES_USER: demobozor
      POSTGRES_PASSWORD: local_only_password # faqat lokal uchun!
      POSTGRES_DB: demobozor
    ports:
      - "5432:5432"
    volumes:
      - db-data:/var/lib/postgresql/data

  api:
    build: ./api # API'ning Dockerfile'i shu papkada
    environment:
      DATABASE_URL: postgres://demobozor:local_only_password@db:5432/demobozor
      SMS_PROVIDER: mock # haqiqiy SMS yubormaslik uchun
      LOG_LEVEL: debug
    ports:
      - "3001:3001"
    depends_on:
      - db

volumes:
  db-data:
```

### QA'ning kundalik Compose buyruqlari

```bash
docker compose up -d                 # hamma servislarni fonda ishga tushirish
docker compose up -d --build         # kod o'zgargan bo'lsa — image'ni qayta yig'ib
docker compose ps                    # qaysi servis ishlayapti
docker compose logs -f api           # api loglarini real vaqtda kuzatish
docker compose logs --since 10m api  # oxirgi 10 daqiqa
docker compose logs -n 50 api        # oxirgi 50 qator
docker compose exec db psql -U demobozor -d demobozor -c "SELECT COUNT(*) FROM orders;"
docker compose restart api           # bitta servisni qayta ishga tushirish
docker compose down                  # konteynerlarni to'xtatib o'chirish (volume qoladi)
```

Test ma'lumotni **noldan** boshlash kerak bo'lsa:

```bash
docker compose down -v               # ⚠️ volume'lar ham o'chadi — bazadagi HAMMA ma'lumot ketadi
docker compose up -d
```

> **⚠️ Diqqat:** `down -v` lokal test bazasi uchun qulay "reset" tugmasi, lekin u nomlangan volume'larni butunlay o'chiradi. Saqlab qolish kerak bo'lgan ma'lumot bo'lsa — avval eksport qiling. Umumiy serverlarda bu buyruqni hech qachon "sinab ko'rish" uchun ishlatmang.

**Testcontainers** — avtotestlar ichidan haqiqiy baza yoki brokerni konteynerda ko'tarib, test tugagach o'chiradigan kutubxonalar oilasi (Java, Go, .NET, Node.js, Python va boshqalar). Integratsiya testlarini "mock" o'rniga haqiqiy PostgreSQL bilan ishlatish uchun qulay — bunda Docker'ga tayanadi.

---

## 10. Loglar: darajalar, structured log, request ID {#loglar}

**Log** — tizim o'zi haqida yozib boradigan kundalik. QA uchun u eng kuchli dalil manbai: foydalanuvchi ko'rmagan narsani log ko'radi.

### Log darajalari

| Daraja            | Qachon yoziladi                                           | DemoBozor misoli                                 |
| ----------------- | --------------------------------------------------------- | ------------------------------------------------ |
| `TRACE` / `DEBUG` | Batafsil texnik tafsilotlar (odatda faqat dev/staging'da) | "Promo KUZ2026 qoidasi tekshirilyapti"           |
| `INFO`            | Oddiy biznes hodisalari                                   | "Buyurtma 11 yaratildi"                          |
| `WARN`            | G'alati, lekin tizim ishlashda davom etyapti              | "To'lov provayderi javob bermadi, qayta urinish" |
| `ERROR`           | Amal bajarilmadi                                          | "Payment 502: provider timeout"                  |
| `FATAL`           | Servis ishlay olmaydi                                     | "Bazaga ulanib bo'lmadi, to'xtayapman"           |

QA uchun signal: foydalanuvchi "hech narsa bo'lmadi" desa, lekin logda `WARN`/`ERROR` bo'lsa — bu bug. Aksincha, oddiy holatda `ERROR` yozilsa — bu ham bug (shovqin haqiqiy xatolarni yashiradi).

### Structured log (JSON) va jq

Zamonaviy servislar logni JSON qatorlar sifatida yozadi — mashina o'qishi oson:

```bash
cat > app.jsonl <<'EOF'
{"time":"2026-10-08T16:25:01.112Z","level":"info","requestId":"req-5a1c","method":"POST","path":"/api/v1/orders","status":201,"ms":184,"userId":9}
{"time":"2026-10-08T16:26:00.020Z","level":"warn","requestId":"req-7f3a","method":"POST","path":"/api/v1/payments","orderId":11,"msg":"retrying provider call"}
{"time":"2026-10-08T16:26:00.871Z","level":"error","requestId":"req-7f3a","method":"POST","path":"/api/v1/payments","status":502,"ms":850,"orderId":11,"msg":"provider timeout"}
{"time":"2026-10-08T16:28:30.004Z","level":"info","requestId":"req-7f3b","method":"POST","path":"/api/v1/payments","status":201,"ms":410,"orderId":11}
{"time":"2026-10-08T16:28:30.010Z","level":"error","requestId":"req-7f3b","orderId":11,"msg":"amount mismatch","expected":135000,"paid":130000}
{"time":"2026-10-08T16:31:40.101Z","level":"warn","requestId":"req-5a20","method":"POST","path":"/api/v1/auth/verify","status":429,"msg":"too many attempts"}
EOF
```

```bash
$ jq -c 'select(.level == "error")' app.jsonl
{"time":"2026-10-08T16:26:00.871Z","level":"error","requestId":"req-7f3a","method":"POST","path":"/api/v1/payments","status":502,"ms":850,"orderId":11,"msg":"provider timeout"}
{"time":"2026-10-08T16:28:30.010Z","level":"error","requestId":"req-7f3b","orderId":11,"msg":"amount mismatch","expected":135000,"paid":130000}

$ jq -r 'select(.requestId == "req-7f3a") | [.time, .level, .msg] | @tsv' app.jsonl
2026-10-08T16:26:00.020Z	warn	retrying provider call
2026-10-08T16:26:00.871Z	error	provider timeout

$ jq -s -c 'group_by(.level) | map({level: .[0].level, count: length})' app.jsonl
[{"level":"error","count":2},{"level":"info","count":2},{"level":"warn","count":2}]

$ jq -c 'select(.ms != null and .ms > 300) | {requestId, path, ms}' app.jsonl
{"requestId":"req-7f3a","path":"/api/v1/payments","ms":850}
{"requestId":"req-7f3b","path":"/api/v1/payments","ms":410}
```

### Request ID — bitta so'rovning "pasporti"

Har bir kiruvchi so'rovga noyob ID beriladi va u shu so'rov o'tgan **barcha** servislar loglariga yoziladi. Ko'p loyihalarda u javob header'ida ham qaytadi (masalan, `X-Request-ID` — keng tarqalgan odat; taqsimlangan tizimlarda W3C standarti bo'lgan `traceparent` header'i ishlatiladi).

QA uchun oltin usul:

1. DevTools Network yoki Proxyman'da muammoli so'rovni topasiz.
2. Javob header'idan request ID'ni olasiz (masalan, `req-7f3a`).
3. Shu ID bo'yicha loglardan butun "hikoya"ni chiqarasiz: retry, timeout, xato.
4. Bug report'ga request ID, vaqt (UTC) va log parchasini qo'shasiz.

Dasturchi sizdan "qachon, qaysi foydalanuvchi, qaysi qurilma?" deb so'ramaydi — ID hammasini bog'laydi.

### Markazlashgan log va xato tizimlari

Production'da loglar fayllarda emas, maxsus tizimlarda yig'iladi. QA odatda ularga brauzer orqali kiradi:

| Vosita               | Nima uchun                                                      | QA'ning odatiy so'rovi                               |
| -------------------- | --------------------------------------------------------------- | ---------------------------------------------------- |
| Grafana Loki         | Loglarni yig'ish va LogQL bilan qidirish                        | `{job="demobozor-api"} \|= "req-7f3a"`               |
| Kibana (Elastic)     | Loglar ustida qidiruv va dashboard'lar                          | `requestId : "req-7f3a"` (KQL)                       |
| Sentry               | Xatolar: stack trace, breadcrumb'lar, qaysi relizda boshlangani | Issue havolasi va event ID'ni bug report'ga qo'shish |
| Firebase Crashlytics | Mobil ilova crash'lari                                          | Crash-free foydalanuvchilar, crash stack trace       |

LogQL'da JSON loglarni maydon bo'yicha filtrlash: `{job="demobozor-api"} | json | level="error"`. Monitoring va alerting chuqurroq — [DevOps: observability](/ios-lessons/devops-observability).

> **⚠️ Diqqat:** Logda parol, to'liq karta raqami, SMS kod yoki access token ko'rsangiz — bu **xavfsizlik bug'i**. Yuqoridagi logda telefon `+99890***4567` ko'rinishida yashirilgan — to'g'ri amaliyot. Batafsil — [QA 24](/ios-lessons/qa-security).

---

## 11. Stack trace va mobil loglar {#stack-trace}

**Stack trace** — dastur yiqilgan paytdagi funksiyalar chaqiruvlari zanjiri. Qo'rqinchli ko'rinadi, lekin o'qish qoidasi oddiy.

Node.js'dagi haqiqiy misol — savatchasi hali yaratilmagan yangi foydalanuvchi checkout qilganda:

```text
file:///app/src/cart.mjs:2
  return cart.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
              ^

TypeError: Cannot read properties of undefined (reading 'items')
    at cartTotal (file:///app/src/cart.mjs:2:15)
    at checkout (file:///app/src/checkout.mjs:4:17)
    at file:///app/server.mjs:5:1
    at ModuleJob.run (node:internal/modules/esm/module_job:430:25)
    at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:661:26)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)

Node.js v24.14.1
```

Qanday o'qiladi:

1. **Xato turi va xabari** — `TypeError: Cannot read properties of undefined (reading 'items')`: `undefined` qiymatdan `.items` o'qilmoqchi bo'lingan.
2. **Birinchi kadr (frame)** — xato aynan qayerda: `cart.mjs`, 2-qator, 15-ustun, `cartTotal` funksiyasi.
3. **Kim chaqirgan** — pastga qarab: `checkout` → `server.mjs`. `node:internal/...` kadrlari Node.js'ning ichki qismi, ularni odatda o'tkazib yuborasiz.
4. **Xulosa** — `user.cart` `undefined` bo'lgan holat ko'zda tutilmagan. Test g'oyasi: "savatchasi bo'sh/yo'q foydalanuvchi checkout'ni bosadi".

Java'da (masalan, Spring backend) stack trace shunday ko'rinadi — bu **namuna**, tuzilishi muhim:

```text
java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because the return value of "uz.demobozor.order.OrderRequest.getPromoCode()" is null
	at uz.demobozor.promo.PromoService.apply(PromoService.java:42)
	at uz.demobozor.order.OrderService.create(OrderService.java:87)
	at uz.demobozor.order.OrderController.createOrder(OrderController.java:35)
	...
```

Bu yerda ham birinchi qator xato turi va sababini aytadi (promo-kod `null`, ya'ni promo-kodsiz buyurtma), keyingi qatorlar — loyiha paketidagi (`uz.demobozor...`) birinchi kadr. Java'da `Caused by:` bilan boshlangan qism ham bo'lishi mumkin — **eng pastdagi** `Caused by` odatda asl sababni ko'rsatadi.

> **💡 Maslahat:** Bug report'ga stack trace'ni **skrinshot emas, matn** sifatida qo'shing (kod bloki ichida). Matnni qidirish, nusxalash va dublikatlarni topish mumkin; skrinshotni — yo'q.

### Mobil loglar

| Platforma     | Qayerdan olinadi                                                     | Buyruq / yo'l                                                                             |
| ------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| iOS Simulator | macOS `log` vositasi simulyator ichida                               | `xcrun simctl spawn booted log stream --level debug --predicate 'process == "DemoBozor"'` |
| iOS qurilma   | Xcode Console yoki macOS **Console** ilovasi (qurilma ulangan holda) | Console.app → qurilmani tanlash → jarayon nomi bilan filtrlash                            |
| iOS crash'lar | Xcode Organizer, TestFlight, Crashlytics                             | Crash log'ni bug report'ga ilova qilish                                                   |
| Android       | `adb logcat` (emulator yoki USB orqali qurilma)                      | `adb logcat "*:E"` — faqat xatolar; `adb logcat -d > logcat.txt` — joriy logni faylga     |

`adb logcat` filtri `tag:daraja` ko'rinishida: `V` (verbose), `D` (debug), `I` (info), `W` (warning), `E` (error), `F` (fatal), `S` (silent). zsh'da `*` belgisi maxsus ma'noga ega, shuning uchun filtrni qo'shtirnoqqa oling: `adb logcat "DemoBozor:D *:S"`. Mobil testlash bo'yicha to'liq — [QA 14](/ios-lessons/qa-mobile-testing).

---

## 12. Amaliy mashqlar {#mashqlar}

Mashqlar uchun 3- va 10-bo'limlardagi `api.log` va `app.jsonl` fayllarini yarating.

### Mashq 1: Bitta to'lovning hikoyasi 🟢

`api.log` dan 11-buyurtma (`order=11`) bilan bog'liq barcha qatorlarni qator raqami bilan chiqaring. Nechta qator bor va ulardan nechtasi `ERROR`?

<details>
<summary>💡 Javobni ko'rish</summary>

```bash
$ grep -n "order=11" api.log
3:2026-10-08T16:26:00.020Z WARN  [req-7f3a] POST /api/v1/payments order=11 provider=UZCARD retry=1
4:2026-10-08T16:26:00.871Z ERROR [req-7f3a] POST /api/v1/payments order=11 status=502 ms=850 msg="provider timeout"
6:2026-10-08T16:28:30.004Z INFO  [req-7f3b] POST /api/v1/payments order=11 provider=UZCARD status=201 ms=410
7:2026-10-08T16:28:30.010Z ERROR [req-7f3b] amount mismatch order=11 expected=135000 paid=130000

$ grep "order=11" api.log | grep -c "ERROR"
2
```

4 ta qator, 2 tasi `ERROR`. E'tibor bering: `order=11` qidiruvi `GET /api/v1/orders/11` qatorini ushlamadi — u yerda format boshqa. Logda bir obyekt turli ko'rinishda yozilishi mumkin, shuning uchun bir nechta qidiruv bilan tekshiring.

</details>

### Mashq 2: Eng sekin so'rovlar 🟡

`api.log` dagi `ms=` qiymatlari bo'yicha eng sekin 3 ta qatorni chiqaring.

<details>
<summary>💡 Javobni ko'rish</summary>

```bash
$ grep -o "\[req-[a-z0-9]*\].*ms=[0-9]*" api.log | awk -F'ms=' '{print $2, $0}' | sort -rn | head -3 | cut -d' ' -f2-
[req-7f3a] POST /api/v1/payments order=11 status=502 ms=850
[req-7f3b] POST /api/v1/payments order=11 provider=UZCARD status=201 ms=410
[req-5a1c] POST /api/v1/orders user=9 status=201 ms=184
```

`awk` har qatorning boshiga `ms` qiymatini qo'yadi, `sort -rn` shu son bo'yicha kamayish tartibida saralaydi, `cut` esa yordamchi sonni olib tashlaydi. Bu ishlaydi, lekin o'qish qiyin. Structured log bo'lsa, xuddi shu vazifa ancha sodda:

```bash
$ jq -s -c 'map(select(.ms != null)) | sort_by(-.ms) | .[:3] | map({requestId, path, ms})' app.jsonl
[{"requestId":"req-7f3a","path":"/api/v1/payments","ms":850},{"requestId":"req-7f3b","path":"/api/v1/payments","ms":410},{"requestId":"req-5a1c","path":"/api/v1/orders","ms":184}]
```

Saboq: matnli logni tahlil qilish mumkin, lekin JSON log va `jq` bilan ish ancha ishonchli. Loyihangiz hali matnli log yozsa — structured log'ga o'tishni taklif qilish ham QA'ning foydali hissasi.

</details>

### Mashq 3: curl + jq 🟢

JSONPlaceholder'dan 1-foydalanuvchining postlari sonini va uning bajarilgan (`completed: true`) vazifalari sonini aniqlang.

<details>
<summary>💡 Javobni ko'rish</summary>

```bash
$ curl -s "https://jsonplaceholder.typicode.com/posts?userId=1" | jq length
10
$ curl -s "https://jsonplaceholder.typicode.com/todos?userId=1&completed=true" | jq length
11
```

Query parametrlari bo'lgan URL'ni qo'shtirnoqqa oling: aks holda `&` belgisini shell "buyruqni fonda ishga tushir" deb tushunadi, `?` esa zsh'da fayl nomi shabloni sifatida talqin qilinishi mumkin.

</details>

### Mashq 4: Smoke skriptni kengaytirish 🟡

`smoke.sh` ga restful-booker'ning `POST /auth` endpoint'i to'g'ri login bilan `token` qaytarishini tekshiradigan qadam qo'shing.

<details>
<summary>💡 Javobni ko'rish</summary>

```bash
token=$(curl -s -X POST "$BASE_URL/auth" \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"password123"}' | jq -r '.token // empty')

if [ -n "$token" ]; then
  echo "✅ /auth → token olindi"
else
  echo "❌ /auth → token yo'q"
  failed=$((failed + 1))
fi
```

`// empty` — `jq`'da "maydon yo'q bo'lsa, bo'sh qaytar" degani; aks holda `jq -r` `null` so'zini chiqarib, tekshiruv aldanib qolardi. Bu qadamni `if [ "$failed" -gt 0 ]` dan **oldin** qo'ying.

</details>

### Mashq 5: Reliz farqi va regression ko'lami 🟡

Dasturchi "v1.3 da faqat README va versiya o'zgardi, regression shart emas" demoqda. 7-bo'limdagi repo'da buni qanday tekshirasiz?

<details>
<summary>💡 Javobni ko'rish</summary>

```bash
git log --oneline v1.2..v1.3      # qaysi commit'lar kirgan
git diff --stat v1.2 v1.3         # qaysi fayllar o'zgargan
git diff v1.2 v1.3 -- delivery.mjs
```

`git diff --stat` `delivery.mjs` faylida 9 qator qo'shilganini ko'rsatadi. Demak yetkazish narxi va muddati — regression ro'yxatida majburiy. Gap bilan emas, dalil bilan gaplashing: diff'ni ko'rsatib, aniq test ssenariylarini taklif qiling.

</details>

### Mashq 6: Build bo'lmaydigan commit'lar 🔴

Katta loyihada bisect paytida ba'zi oraliq commit'lar umuman build bo'lmaydi. `git bisect run` uchun skriptni qanday yozasiz, toki bunday commit'lar "bad" deb belgilanmasin?

<details>
<summary>💡 Javobni ko'rish</summary>

Build xatosida skript **125** bilan chiqishi kerak — git bu commit'ni o'tkazib yuboradi (`git bisect skip` kabi):

```bash
#!/usr/bin/env bash
# bisect-check.sh
npm ci --silent || exit 125          # bog'liqliklar o'rnatilmadi → test qilib bo'lmaydi
npm run build --silent || exit 125   # build bo'lmadi → o'tkazib yuboramiz
node ../qa-check.mjs                 # 0 = good, 1 = bad
```

Agar build xatosida `exit 1` qilinsa, git bu commit'ni "bad" deb hisoblaydi va noto'g'ri commit'ni aybdor qilib ko'rsatishi mumkin.

</details>

### Mashq 7: Lokal bazani noldan boshlash 🟡

Docker Compose'dagi DemoBozor muhitida test ma'lumotlar aralashib ketdi. Bazani butunlay tozalab, qayta ko'taring va API'ning oxirgi 50 qator logini ko'ring. Qaysi buyruq xavfli va nega?

<details>
<summary>💡 Javobni ko'rish</summary>

```bash
docker compose down -v      # konteynerlar + nomlangan volume'lar (baza fayllari) o'chadi
docker compose up -d
docker compose logs -n 50 api
```

Xavfli qadam — `down -v`: u `db-data` volume'ini o'chiradi, bazadagi barcha ma'lumot qaytarib bo'lmas tarzda yo'qoladi. Lokal o'quv bazasi uchun bu aynan maqsad, lekin umumiy muhitlarda bunday buyruq faqat jamoa kelishuvi bilan ishlatiladi.

</details>

### Mashq 8: Stack trace'dan test g'oyasigacha 🟡

11-bo'limdagi Java namunasini o'qing. Xato qaysi faylning qaysi qatorida, qaysi foydalanuvchi harakati uni keltirib chiqargan bo'lishi mumkin va qanday test case yozasiz?

<details>
<summary>💡 Javobni ko'rish</summary>

- **Joy:** `PromoService.java`, 42-qator, `apply` metodi (loyiha paketidagi birinchi kadr).
- **Sabab:** `getPromoCode()` `null` qaytargan, kod esa unga `toUpperCase()` chaqirgan — promo-kodsiz buyurtma holati ko'zda tutilmagan.
- **Harakat:** foydalanuvchi promo-kod maydonini bo'sh qoldirib buyurtma bergan (yoki mobil ilova maydonni umuman yubormagan).
- **Test case'lar:** promo-kodsiz buyurtma (maydon yo'q), bo'sh satr `""`, faqat probellar `"   "`, kichik harfli `kuz2026` — hammasida buyurtma muvaffaqiyatli yaratilishi yoki tushunarli validatsiya xabari chiqishi kerak, 500 emas.

</details>

---

## O'zingizni tekshiring {#checklist}

- [ ] `grep -n`, `-c`, `-i`, `-v`, `-E`, `-C` flaglarini va pipe'ni ishlata olaman
- [ ] `>` va `>>` farqini bilaman, dalilni faylga saqlay olaman
- [ ] `curl` bilan status kod va JSON javob olaman, `jq` bilan filtrlayman
- [ ] Exit code nima ekanini va CI uchun nega muhimligini tushuntira olaman
- [ ] Feature branch'ga o'tib test qila olaman, `git log v1..v2` bilan regression ko'lamini aniqlayman
- [ ] `git bisect` (qo'lda va `run` bilan) yordamida aybdor commit'ni topa olaman
- [ ] Docker Compose bilan muhitni ko'tarib, loglarini o'qiyman; `down -v` xavfini bilaman
- [ ] Request ID bo'yicha logdan dalil yig'ib, stack trace'ni bug report'ga matn sifatida qo'shaman

---

## Xulosa {#xulosa}

- Terminal — QA'ning eng tez asbobi: `grep`, pipe, `tail -f`, `curl` va `jq` bilan log va API'ni sekundlarda tahlil qilasiz.
- Exit code (`0` = muvaffaqiyat) skriptlar, CI va `git bisect run`'ning umumiy tili.
- Git QA uchun — vaqt mashinasi: branch'ni test qilish, reliz farqini ko'rish (`git log v1.2..v1.3`), `blame` va `log -S` bilan kontekst topish.
- `git bisect` regressiyani keltirib chiqargan commit'ni binary search bilan topadi; tekshiruv skript bo'lsa — to'liq avtomatik.
- Muhitlar, feature flag'lar va `.env` konfiguratsiyasini tushunish "nega staging'da ishlaydi-yu, prod'da yo'q?" savoliga javob beradi. Sirlar Git'ga tushmaydi.
- Docker Compose lokal muhitni bir buyruq bilan ko'taradi; `down -v` — kuchli, lekin qaytarib bo'lmas "reset".
- Loglar va request ID — bug report'ning eng kuchli dalili; stack trace'ni matn sifatida qo'shing, logda sirlar ko'rinsa — bu alohida bug.
- **Keyingi dars:** [QA 14: Mobil ilovalarni testlash — iOS va Android farqlari, Simulator, ADB, TestFlight va checklist](/ios-lessons/qa-mobile-testing)

## Manbalar {#manbalar}

1. [Apple — Use zsh as the default shell on your Mac](https://support.apple.com/en-us/102360)
2. [Microsoft — Install WSL](https://learn.microsoft.com/en-us/windows/wsl/install)
3. [grep(1) — Linux manual page](https://man7.org/linux/man-pages/man1/grep.1.html)
4. [tail(1) — Linux manual page](https://man7.org/linux/man-pages/man1/tail.1.html)
5. [curl — man page](https://curl.se/docs/manpage.html)
6. [jq — Manual](https://jqlang.org/manual/) va [jq — Download](https://jqlang.org/download/)
7. [Git — git-bisect documentation](https://www.kernel.org/pub/software/scm/git/docs/git-bisect.html)
8. [Git — git-switch](https://www.kernel.org/pub/software/scm/git/docs/git-switch.html), [git-blame](https://www.kernel.org/pub/software/scm/git/docs/git-blame.html), [git-stash](https://www.kernel.org/pub/software/scm/git/docs/git-stash.html)
9. [GitHub CLI — gh pr checkout](https://cli.github.com/manual/gh_pr_checkout)
10. [The Twelve-Factor App — Config](https://12factor.net/config)
11. [Docker — Compose application model (compose.yaml)](https://docs.docker.com/compose/intro/compose-application-model/)
12. [Docker — docker compose logs](https://docs.docker.com/reference/cli/docker/compose/logs/) va [docker compose down](https://docs.docker.com/reference/cli/docker/compose/down/)
13. [Docker Desktop](https://docs.docker.com/desktop/)
14. [Testcontainers](https://testcontainers.com/)
15. [W3C — Trace Context](https://www.w3.org/TR/trace-context/)
16. [Grafana Loki — Log queries (LogQL)](https://grafana.com/docs/loki/latest/query/log_queries/)
17. [Elastic — KQL](https://www.elastic.co/docs/explore-analyze/query-filter/languages/kql)
18. [Sentry — Issues](https://docs.sentry.io/product/issues/)
19. [Firebase Crashlytics](https://firebase.google.com/docs/crashlytics)
20. [Android Developers — Logcat](https://developer.android.com/tools/logcat)
21. [Apple — Console User Guide](https://support.apple.com/guide/console/welcome/mac)
