Azimjon Abdurasulov

DevOps 3: Release engineering — versiyalash, phased release va feature flag'lar

🛠️ DevOps Engineering

DevOps 3: Release engineering — versiyalash, phased release va feature flag'lar

Holat: 2026-yil sentabr. Bu dars "kodni qanday xavfsiz chiqaramiz?" savoliga javob beradi: versiya raqamlari, branch strategiyasi, bosqichma-bosqich reliz, feature flag va majburiy yangilanish.

Mundarija

  1. Versiya va build raqami
  2. DORA: reliz sifatini o'lchash
  3. Branch strategiyasi mobil ilova uchun
  4. Release train va hotfix
  5. Phased release — App Store'dagi "canary"
  6. Feature flag va remote config
  7. Kill switch va majburiy yangilanish
  8. Reliz oldidan checklist
  9. Xulosa
  10. Manbalar

1. Versiya va build raqami {#versiya}

Har bir iOS build'da ikkita raqam bor:

Info.plist kalitiBuild SettingKim ko'radiQoida
CFBundleShortVersionStringMARKETING_VERSIONFoydalanuvchi (App Store'da)SemVer: MAJOR.MINOR.PATCH, masalan 2.4.1
CFBundleVersionCURRENT_PROJECT_VERSIONFaqat siz va AppleBir versiya ichida har bir yuklashda o'sib borishi shart

SemVer qisqacha: MAJOR — katta o'zgarish (masalan butun dizayn yangilandi), MINOR — yangi funksiya, PATCH — faqat tuzatish.

Raqamlarni terminaldan o'zgartirish uchun agvtool bor. U ishlashi uchun target'da Versioning System = Apple Generic bo'lishi kerak:

# build raqamini 42 ga qo'yish (barcha target'larda)
xcrun agvtool new-version -all 42

# foydalanuvchi ko'radigan versiyani 2.5.0 ga qo'yish
xcrun agvtool new-marketing-version 2.5.0

# joriy qiymatlarni ko'rish
xcrun agvtool what-version
xcrun agvtool what-marketing-version

CI'da build raqamini qo'lda boshqarish shart emas. ExportOptions.plistdagi manageAppVersionAndBuildNumber (standart qiymati YES) yuklash paytida raqamni Xcode'ning o'ziga boshqartiradi — batafsil DevOps 1 darsida.


2. DORA: reliz sifatini o'lchash {#dora}

DORA (DevOps Research and Assessment) — Google tarkibidagi tadqiqot guruhi. U jamoaning yetkazib berish sifatini to'rtta metrika bilan o'lchaydi (dora.dev):

MetrikaSavolMobil ilovada qanday o'lchanadi
Deployment frequencyQanchalik tez-tez chiqaramiz?App Store'ga reliz soni (oyiga)
Lead time for changesCommit'dan foydalanuvchigacha qancha vaqt?PR merge → App Store'da "Ready for Sale"
Change failure rateRelizlarning qanchasi tuzatishni talab qildi?Hotfix talab qilgan relizlar ulushi
Failed deployment recovery timeBuzilgan reliz qancha vaqtda tiklandi?Muammo topilgandan hotfix chiqquncha

2025-yilgi DORA hisoboti "State of AI-assisted Software Development" deb nomlandi (~5 000 ishtirokchi). Unda AI ishlatish 90%ga yetdi (yiliga +14 foiz punkt), eski "Elite/High/Medium/Low" darajalari o'rniga esa jamoa arxetiplari kiritildi. Asosiy xulosa: AI — kuchaytirgich. Yaxshi jarayonli jamoani tezlashtiradi, tartibsiz jamoaning muammolarini esa kattalashtiradi. Manba: Google — DORA 2025.


3. Branch strategiyasi mobil ilova uchun {#branch}

Web'da trunk-based development (hamma kichik PR'lar bilan to'g'ridan-to'g'ri mainga) keng tarqalgan. Mobil ilovada esa bitta farq bor: reliz App Review'dan o'tadi va foydalanuvchida eski versiya uzoq qoladi. Shuning uchun amaliy gibrid yaxshi ishlaydi:

main  ──●──●──●──●──●──●──●──●──●──●──►   (kichik PR'lar, har kuni)
            \                 \
             release/2.4 ──●──●    release/2.5 ──●   (faqat reliz oldidan: freeze + tuzatishlar)
                           │
                        tag v2.4.0 → App Store
  • Kundalik ish — mainda, qisqa umrli branch'lar orqali.
  • Reliz oldidan release/x.y ajratiladi. Unga faqat tuzatishlar kiradi (code freeze).
  • Tugallanmagan funksiyalar mainda feature flag ortida yashiringan holda turadi (6-bo'lim).

4. Release train va hotfix {#release-train}

Release train — reliz belgilangan jadval bo'yicha chiqadi (masalan har 2 haftada). "Poyezd" vaqtida jo'naydi: tayyor bo'lmagan funksiya keyingi reyse qoladi. Foydasi — reliz sanasi bashorat qilinadi va jamoa har safar "chiqaramizmi?" deb bahslashmaydi.

Hotfix oqimi (production'da jiddiy bug):

# 1. Oxirgi reliz tag'idan hotfix branch
git switch -c hotfix/2.4.1 v2.4.0

# 2. main'dagi tuzatish commit'ini olib kelish (yoki shu yerda tuzatish)
FIX_SHA="a1b2c3d"   # git log --oneline main dan tuzatish commit'ining SHA'si
git cherry-pick "$FIX_SHA"

# 3. PATCH versiyani oshirib tag qo'yish — CI TestFlight'ga yuboradi
xcrun agvtool new-marketing-version 2.4.1
git commit -am "bumped version to 2.4.1"
git tag v2.4.1
git push origin hotfix/2.4.1 v2.4.1

Kritik holatda App Store Connect'dan expedited review (tezlashtirilgan ko'rib chiqish) so'rash mumkin. Bu imkoniyatni faqat haqiqatan jiddiy muammolar uchun ishlating.


5. Phased release — App Store'dagi "canary" {#phased}

Backend'da canary deploy — yangi versiyani avval trafikning kichik qismiga berish. App Store'dagi analogi — Phased Release for Automatic Updates. Yangilanish 7 kun davomida avtomatik yangilanishi yoqilgan foydalanuvchilarga bosqichma-bosqich tarqaladi:

Kun1234567
Foydalanuvchilar ulushi1%2%5%10%20%50%100%

Muhim nuanslar:

  • Faqat update'larga tegishli, birinchi relizga emas.
  • Foydalanuvchi App Store'ga kirib ilovani qo'lda yangilashi mumkin — phased release unga to'sqinlik qilmaydi.
  • Relizni pauza qilish mumkin (umumiy pauza muddati cheklangan).
  • Bu rollback emas. Yangilab olgan foydalanuvchi eski versiyaga qaytmaydi, shuning uchun haqiqiy himoya — feature flag va kill switch.

Manba: Apple — Release a version update in phases, phased release guide.

Phased release vaqtida nimani kuzatasiz? Crash-free sessions, hang rate, asosiy funnel (masalan xarid) konversiyasi. Batafsil DevOps 3 darsida.


6. Feature flag va remote config {#feature-flags}

Feature flag — kod binary'da bor, lekin uni yoqish-o'chirish serverdan boshqariladi. Bu deploy (kod foydalanuvchiga yetdi) bilan release (funksiya ochildi)ni ajratadi. Mobil ilovada bu ayniqsa qadrli: App Review'ni kutmasdan funksiyani o'chirib, yoqib yoki foizlar bo'yicha ochib borasiz.

Asbob tanlash (2026)

AsbobXususiyati
Firebase Remote Config2026-09-01 dan foydalanish bo'yicha narxlanadi: kuniga 100 000 fetch'gacha bepul, 100 ming–10 mln/kun oralig'ida har 10 000 fetch uchun $0.06, 10 mln/kun'dan yuqori qismi uchun $0.01 (pricing)
LaunchDarklyEnterprise standart, faqat SaaS
Statsig2025-09-02 da OpenAI ~$1.1 mlrd'ga sotib oldi. 2026-yil mayda Amplitude bilan brend va mijozlar bo'yicha kelishuv bo'ldi — tanlashdan oldin joriy holatini tekshiring (TechCrunch)
GrowthBookOchiq manba, o'zingiz host qilishingiz mumkin
UnleashOchiq manba, self-host

SDK'dan mustaqil flag qatlami (Swift)

Qaysi provayderni tanlashingizdan qat'i nazar, ilova kodi provayderga to'g'ridan-to'g'ri bog'lanmasin. Protokol orqali ajrating: ertaga Firebase'dan GrowthBook'ga o'tsangiz, faqat bitta klass o'zgaradi.

import Foundation

enum FeatureFlag: String, CaseIterable, Sendable {
    case newOnboarding = "new_onboarding"
    case aiLessonSummary = "ai_lesson_summary"

    /// Serverdan javob kelmasa (birinchi ishga tushish, internet yo'q) shu qiymat ishlaydi.
    var defaultValue: Bool {
        switch self {
        case .newOnboarding: false
        case .aiLessonSummary: false
        }
    }
}

/// Har qanday provayder (Firebase, GrowthBook, o'z serveringiz) shu protokolni bajaradi.
protocol RemoteFlagProvider: Sendable {
    func fetchFlags() async throws -> [String: Bool]
}

@MainActor
final class FeatureFlags {
    private var values: [String: Bool]
    private let provider: any RemoteFlagProvider
    private let cacheKey = "feature_flags.cache"

    init(provider: any RemoteFlagProvider) {
        self.provider = provider
        // Oxirgi muvaffaqiyatli javob — keyingi ishga tushishda darhol ishlatiladi.
        values = UserDefaults.standard.dictionary(forKey: cacheKey) as? [String: Bool] ?? [:]
    }

    func isEnabled(_ flag: FeatureFlag) -> Bool {
        values[flag.rawValue] ?? flag.defaultValue
    }

    func refresh() async {
        do {
            let fresh = try await provider.fetchFlags()
            values = fresh
            UserDefaults.standard.set(fresh, forKey: cacheKey)
        } catch {
            // Tarmoq xatosi: eski (cache) yoki default qiymatlar bilan davom etamiz.
        }
    }
}

Nega default va cache muhim? Birinchi ishga tushishda server javobi hali kelmagan bo'ladi. Default qiymat bo'lmasa, UI "sakraydi" (flicker) — avval bir holat ko'rinadi, keyin boshqasi. Qoida: default qiymat har doim xavfsiz holat bo'lsin, ya'ni yangi funksiya o'chiq.


7. Kill switch va majburiy yangilanish {#force-update}

Kill switch — muammoli funksiyani serverdan darhol o'chiradigan flag. Yangi build va review kerak emas. Har bir xavfli yangi funksiya (to'lov oqimi, AI funksiyasi, yangi sinxronizatsiya) kill switch bilan chiqsin.

Majburiy yangilanish — backend eski, buzuq client'ni endi qo'llab-quvvatlamaydi. Eng ishonchli usul: o'z serveringizdan min_supported_version qaytarish. Yumshoq eslatma ("yangi versiya chiqdi") uchun esa Apple'ning ochiq iTunes Lookup APIsini ishlatish mumkin:

import Foundation

struct AppStoreLookup: Decodable {
    struct Entry: Decodable {
        let version: String
        let trackViewUrl: String
    }
    let resultCount: Int
    let results: [Entry]
}

enum UpdateStatus: Sendable {
    case upToDate
    case updateAvailable(storeURL: URL)
}

/// App Store'dagi versiyani joriy versiya bilan solishtiradi.
func checkForUpdate(bundleID: String, currentVersion: String) async throws -> UpdateStatus {
    var components = URLComponents(string: "https://itunes.apple.com/lookup")!
    components.queryItems = [
        URLQueryItem(name: "bundleId", value: bundleID),
        URLQueryItem(name: "country", value: "uz"),
    ]
    let (data, _) = try await URLSession.shared.data(from: components.url!)
    let lookup = try JSONDecoder().decode(AppStoreLookup.self, from: data)

    guard let entry = lookup.results.first,
          let storeURL = URL(string: entry.trackViewUrl) else {
        return .upToDate // topilmadi — foydalanuvchini bezovta qilmaymiz
    }
    // .numeric: "2.10.0" > "2.9.3" deb to'g'ri solishtiradi
    let isNewer = entry.version.compare(currentVersion, options: .numeric) == .orderedDescending
    return isNewer ? .updateAvailable(storeURL: storeURL) : .upToDate
}

Ehtiyot bo'ling:

  • Lookup javobi keshlangan bo'lishi mumkin. Reliz chiqqandan so'ng yangi versiya u yerda biroz kechikib paydo bo'ladi. Shuning uchun majburiy blok uchun o'z serveringizdagi qiymatga tayaning.
  • resultCount == 0 — ilova tanlangan storefront'da topilmadi. Bu xato emas, foydalanuvchini bloklamang.

8. Reliz oldidan checklist {#checklist}

  • [ ] release/x.y branch, versiya va build raqami to'g'ri
  • [ ] Barcha testlar va lint CI'da yashil
  • [ ] Yangi SDK bo'lsa — privacy manifest ko'rib chiqilgan
  • [ ] Xavfli funksiyalar feature flag / kill switch ortida
  • [ ] TestFlight'da internal test o'tgan (asosiy oqimlar qo'lda tekshirilgan)
  • [ ] "What's New" matni barcha tillarda tayyor
  • [ ] Phased release yoqilgan, kuzatiladigan metrikalar dashboard'i tayyor
  • [ ] Hotfix rejasi: kim, qaysi branch'dan, qanday tag bilan

9. Xulosa {#xulosa}

  • CFBundleShortVersionString — foydalanuvchi uchun (SemVer), CFBundleVersion — har yuklashda o'sadi.
  • DORA'ning 4 metrikasi mobil jamoa uchun ham ishlaydi: chastota, lead time, failure rate, tiklanish vaqti.
  • Mobil uchun gibrid: kundalik ish mainda, reliz oldidan qisqa release/x.y.
  • Phased release (1%→100%, 7 kun) — monitoring oynasi, rollback emas.
  • Feature flag deploy'ni release'dan ajratadi. Default qiymat har doim xavfsiz bo'lsin, provayder protokol ortida tursin.

Manbalar {#manbalar}

  1. DORA — The four keys
  2. Google — 2025 DORA report
  3. Apple — Release a version update in phases
  4. Apple QA1827 — agvtool
  5. Firebase Remote Config pricing
  6. TechCrunch — OpenAI acquires Statsig
  7. Apple — iTunes Search API

Markdown manba

iOS Lessons