Free lesson · LLMOps Engineering

AI પ્રોજેક્ટ્સ માટે trunk-based development અમલમાં મૂકો

તમે GKE પર AI/ML પ્રોજેક્ટ્સ માટે ઑપ્ટિમાઇઝ કરેલી Git branching strategy સેટ અપ કરશો. એક trunk-based workflow કૉન્ફિગર કરો જેમાં main હંમેશા deployable રહે. નામકરણની પરંપરાઓ સાથે ટૂંકા આયુષ્યની feature branches બનાવો: feature/add-prompt-template, fix/embedding-pipeline, config/model-parameters. Branch lifecycle અમલમાં મૂકો: main માંથી બનાવો, commits push કરો, PR ખોલો, main માં squash merge કરો, branch ડિલીટ કરો. Git ને ડિફૉલ્ટ રૂપે rebase વાપરવા માટે કૉન્ફિગર કરો (git config pull.rebase true). Prompt template માં ફેરફાર સાથે workflow ને end-to-end દર્શાવો.

Course: DevOps Foundations for GenAI Engineers · Chapter 1 · Git Workflows for AI Teams

Free to read — no subscription required.

પરિચય

જ્યારે તમે એક અઠવાડિયા સુધી ભટકતી રહેતી ફીચર બ્રાન્ચમાંથી prompts, training JSONL અને model configs શિપ કરો છો, ત્યારે merge સમયે તમને ખબર પડે છે કે Git બે એન્જિનિયરોનું સમાધાન કરી શકતું નથી જેમણે એક જ ફાઇલમાં training rows ઉમેરી હોય અથવા અલગ-અલગ બ્રાન્ચ પર એક જ prompt version બમ્પ કર્યું હોય. આ conflict સપાટી merge કરી શકાય તેવી નથી: દરેક JSONL લાઇન અર્થની દૃષ્ટિએ સ્વતંત્ર છે, અને prompt registry એન્ટ્રીઓ ચોક્કસ સમયબિંદુની હકીકતો છે, three-way merge કરવા માટેનું ટેક્સ્ટ નહીં. જે ટીમો બ્રાન્ચને 48 કલાકથી વધુ જૂની થવા દે છે તે આ કિંમત ગુમાવેલા sprint દિવસો અને બગડેલા merge પછી eval scores માં શાંત regressions ના રૂપે ચૂકવે છે.

આ પાઠના અંતે તમે AI કોડબેઝ પર trunk-based શિસ્ત લાગુ કરી શકશો: બ્રાન્ચને ઉદ્દેશ મુજબ નામ આપો (feat, data, fix, infra, deps), Git hook વડે push સમયે નામો અને ઉંમર validate કરો, અને બ્રાન્ચ પ્રકાર દીઠ merge strategy configure કરો જેથી prompt અને data ફેરફારો atomic અને bisectable રહે.

મુખ્ય પરિભાષા

  • Trunk — એકમાત્ર શેર કરેલી બ્રાન્ચ (સામાન્ય રીતે main) જે હંમેશા deployable હોય છે; દરેક commit schema, evaluation અને infrastructure validation ટ્રિગર કરે છે, તેથી trunk એ prompts, models અને configs માટે source of truth છે.
  • ટૂંકા-ગાળાની ફીચર બ્રાન્ચ — એવી બ્રાન્ચ જે trunk માં પાછી merge થતાં પહેલાં વધુમાં વધુ 48 કલાક જીવે છે; આ સમયમર્યાદા જ prompt અને JSONL drift ને એટલું નાનું રાખે છે કે તેને યાંત્રિક રીતે ઉકેલી શકાય.
  • Squash merge — ફીચર બ્રાન્ચ પરના દરેક commit ને trunk પર એક atomic commit માં સંકોચે છે, જેથી git bisect "add summarization prompt v3" જેવા અર્થપૂર્ણ એકમો પર પહોંચે, WIP saves પર નહીં.
  • Branch protection rule — remote (GitHub/GitLab) પર server-side નીતિ જે branch pattern દીઠ required checks અને merge strategy પર merges ને gate કરે છે, જેથી local hook બાયપાસ થાય તો પણ નીતિ ટકી રહે.
  • Prompt registry — trunk પર ટ્રેક કરેલી JSON ફાઇલ (દા.ત. prompt_registry.json) જે prompt IDs ને version numbers અને file paths સાથે મેપ કરે છે; "બ્રાન્ચ દીઠ એક version bump" નિયમ regressions ને સરળતાથી શોધી શકાય તેવા બનાવે છે.

વિભાવનાઓ

ધ્યેય એ છે કે integration સતત રહે અને prompt/data ફેરફારો atomic રહે. ત્રણ વિચારો ભેગા મળીને ત્યાં પહોંચાડે છે: ઉદ્દેશ એન્કોડ કરતી branch taxonomy, drift ને મર્યાદિત રાખતી 48-કલાકની આયુ, અને auditability જાળવતી શ્રેણી દીઠ merge strategy. આ વિચારોને કાર્યરત કરતા branch validator અને pre-push hook કોડમાં દર્શાવ્યા છે (જુઓ કોડ વૉકથ્રૂ).

AI કોડબેઝ માટે ટૂંકા-ગાળાની બ્રાન્ચ શા માટે મહત્વની છે

AI રિપોઝિટરીઓમાં ત્રણ પ્રકારના artifacts હોય છે જે લાંબા-ગાળાની બ્રાન્ચને તોડી નાખે છે. Prompt અને config ફાઇલો શેર કરેલા બ્લોક છે જ્યાં સમાંતર ફેરફારો ટકરાય છે. JSONL training ફાઇલો append થઈને વધે છે, અને બે બ્રાન્ચ અલગ-અલગ rows ઉમેરે તો ઓવરલેપ થતા diffs બને છે જેને Git ફ્લેગ કરે છે પણ યોગ્ય રીતે ઉકેલી શકતું નથી — reproducibility માટે લાઇનનો ક્રમ મહત્વનો છે. Dependency graphs ઘટ્ટ હોય છે: serving config preprocessing પર નિર્ભર છે જે feature-store schema પર નિર્ભર છે, તેથી આ ત્રણમાંથી કોઈ પણ અલગથી વિકસે તો પીડાદાયક rebase નિશ્ચિત છે.

બ્રાન્ચની આયુ 48 કલાકે મર્યાદિત કરવાથી conflict window સંકોચાય છે. Work-in-progress પાર્ક કરેલી બ્રાન્ચ પર નહીં, પણ trunk પર feature flags પાછળ રહે છે. Renovate અને Dependabot PRs સ્વચ્છ રીતે લેન્ડ થાય છે કારણ કે કોઈ માનવ બ્રાન્ચ એટલી જૂની નથી હોતી કે તે લાગુ થતાં પહેલાં ઊંડા rebase ની જરૂર પડે.

Branch taxonomy ઉદ્દેશ એન્કોડ કરે છે

નામકરણ પરંપરા બ્રાન્ચ નામોને hooks અને CI માટે routable metadata માં ફેરવે છે. દરેક બ્રાન્ચ પ્રકાર અલગ validation lane ટ્રિગર કરે છે: feat/prompt-* eval suites ચલાવે છે, data/* schema અને row-count checks ચલાવે છે, fix/* સંપૂર્ણ regression ચલાવે છે, infra/* IaC plan/apply ચલાવે છે, deps/* build + dependency audit ચલાવે છે.

Loading diagram...

ત્રણ ટૂંકા-ગાળાની બ્રાન્ચ એક જ sprint cycle માં trunk માં પાછી merge થાય છે. કોઈ બ્રાન્ચ બીજી સાથે એક કામકાજના દિવસથી વધુ સહઅસ્તિત્વ ધરાવતી નથી, અને આ જ બાબત JSONL અને prompt ફાઇલોમાં પીડાદાયક merge conflicts સર્જતા drift ને રોકે છે.

Squash merge વિરુદ્ધ merge commit

feat, data, fix અને infra બ્રાન્ચ માટે squash merge ડિફૉલ્ટ છે કારણ કે trunk atomic, bisectable ફેરફારોની શ્રેણી તરીકે વંચાવું જોઈએ — "add summarization prompt v3", નહીં કે "wip" → "fix typo" → "actually fix it". deps/* હેઠળની બ્રાન્ચ સ્પષ્ટ અપવાદ છે: Renovate અને Dependabot commit messages માં structured changelog metadata (package name, version range, changelog URL) એમ્બેડ કરે છે જેને downstream audit tooling પાર્સ કરે છે, તેથી તે બ્રાન્ચ સાચા merge commit સાથે merge થાય છે. Branch protection એવી રીતે configure કરો કે પહેલા ચાર prefixes માટે squash જરૂરી હોય અને merge commits ફક્ત deps/* માટે જ મંજૂર હોય.

બ્રાન્ચ દીઠ એક prompt bump

દરેક ફીચર બ્રાન્ચ prompt_registry.json માં ચોક્કસ એક prompt version વધારે છે અને તેની સાથે તેના eval results શિપ કરે છે. બ્રાન્ચ ટૂંકી અને scope ચુસ્ત હોવાથી, registry conflicts અલગ-અલગ keys પર એકસાથે થતા increments સુધી સીમિત રહે છે — 30-સેકન્ડનું સમાધાન, અડધા દિવસની તપાસ નહીં. જ્યારે deploy પછી કોઈ eval metric ઘટે છે, ત્યારે trunk પર git log --oneline ચોક્કસ એક prompt ફેરફાર તરફ નિર્દેશ કરે છે.

કોડ વૉકથ્રૂ

નીચેના બે snippets વિભાવનાઓ વિભાગમાંથી branch taxonomy, 48-કલાકની આયુ અને local enforcement layer ને કાર્યરત કરે છે. પહેલો regex patterns અને age check સાથે validator વ્યાખ્યાયિત કરે છે; બીજો તેને pre-push hook માં જોડે છે જેથી જૂની અથવા pattern બહારની બ્રાન્ચ remote સુધી પહોંચે તે પહેલાં નીતિ લાગુ થાય.

Code snippetpython
1import re 2import subprocess 3from datetime import datetime, timezone 4from dataclasses import dataclass 5from typing import Optional 6 7BRANCH_PATTERNS = { 8 "feature": re.compile(r"^feat/(prompt|model|pipeline|serve)-[\w-]{3,50}$"), 9 "data": re.compile(r"^data/(training|eval|validation)-[\w-]{3,50}$"), 10 "fix": re.compile(r"^fix/[\w-]{3,50}$"), 11 "infra": re.compile(r"^infra/(gke|helm|terraform)-[\w-]{3,50}$"), 12 "deps": re.compile(r"^deps/(renovate|dependabot)-[\w-]{3,50}$"), 13} 14MAX_BRANCH_AGE_HOURS = 48 15 16@dataclass 17class BranchMetadata: 18 category: str 19 is_valid: bool 20 age_hours: Optional[float] = None 21 is_stale: bool = False 22 23def validate_branch_name(branch: str) -> BranchMetadata: 24 for category, pattern in BRANCH_PATTERNS.items(): 25 if pattern.match(branch): 26 return BranchMetadata(category=category, is_valid=True) 27 return BranchMetadata(category="unknown", is_valid=False) 28 29def get_branch_age_hours(branch: str) -> Optional[float]: 30 result = subprocess.run( 31 ["git", "log", "-1", "--format=%cI", branch], 32 capture_output=True, text=True, 33 ) 34 if result.returncode != 0: 35 return None 36 created = datetime.fromisoformat(result.stdout.strip()) 37 return (datetime.now(timezone.utc) - created).total_seconds() / 3600 38 39def enforce_branch_policy(branch: str) -> BranchMetadata: 40 metadata = validate_branch_name(branch) 41 if not metadata.is_valid: 42 return metadata 43 metadata.age_hours = get_branch_age_hours(branch) 44 if metadata.age_hours is not None and metadata.age_hours > MAX_BRANCH_AGE_HOURS: 45 metadata.is_stale = True 46 return metadata

BRANCH_PATTERNS વિભાવનાઓ વિભાગની taxonomy એન્કોડ કરે છે: subcategory tokens (prompt, training, gke, વગેરે) એન્જિનિયરોને બ્રાન્ચ નામમાં ઉદ્દેશ જાહેર કરવા ફરજ પાડે છે. MAX_BRANCH_AGE_HOURS = 48 એ નીતિનું નિયંત્રણ છે — તેને વધારવાથી ટૂંકા-ગાળાની શિસ્ત ઢીલી થાય છે. enforce_branch_policy બે checks ને જોડે છે; get_branch_age_hours બ્રાન્ચ tip ની committer date (ISO 8601) વાંચે છે અને કલાકોને float તરીકે પરત કરે છે, બ્રાન્ચ ન મળે તો error ફેંકવાને બદલે None પરત કરે છે.

Code snippetpython
1import sys 2import subprocess 3from branch_validator import enforce_branch_policy 4 5def get_current_branch() -> str: 6 result = subprocess.run( 7 ["git", "rev-parse", "--abbrev-ref", "HEAD"], 8 capture_output=True, text=True, 9 ) 10 return result.stdout.strip() 11 12def main() -> int: 13 branch = get_current_branch() 14 if branch == "main": 15 return 0 16 metadata = enforce_branch_policy(branch) 17 if not metadata.is_valid: 18 print(f"ERROR: '{branch}' does not match any allowed pattern.") 19 print("Allowed: feat/(prompt|model|pipeline|serve)-..., data/(training|eval|validation)-...,") 20 print(" fix/..., infra/(gke|helm|terraform)-..., deps/(renovate|dependabot)-...") 21 return 1 22 if metadata.is_stale: 23 print(f"WARNING: '{branch}' is {metadata.age_hours:.1f}h old; merge or delete.") 24 return 1 25 print(f"Branch policy OK: [{metadata.category}] {branch}") 26 return 0 27 28if __name__ == "__main__": 29 sys.exit(main())

Hook git rev-parse --abbrev-ref HEAD વડે વર્તમાન બ્રાન્ચ શોધે છે, main પર short-circuit કરે છે જેથી trunk પરના CI merge commits અસરગ્રસ્ત ન થાય, અને અમાન્ય નામો અથવા જૂની બ્રાન્ચ બંને માટે exit code 1 પરત કરે છે. તેને .git/hooks/pre-push તરીકે (અથવા pre-commit framework દ્વારા pre-push stage હેઠળ) ઇન્સ્ટોલ કરો, અને તેને server-side branch protection rules સાથે જોડો જે feat/*, data/*, fix/*, infra/* માટે squash merges જરૂરી બનાવે અને merge commits ફક્ત deps/* માટે જ મંજૂર કરે.

તમને ખબર પડશે કે તે કામ કરે છે જ્યારે (1) wip-stuff push કરવું allowed-patterns મદદ ટેક્સ્ટ સાથે નકારાય, (2) 50-કલાક જૂની feat/prompt-foo push કરવી કલાકોમાં ઉંમર સાથે નકારાય, (3) માન્ય, તાજી feat/prompt-foo push કરવાથી OK લાઇન છપાય, અને (4) trunk પર prompt_registry.json દરેક merge થયેલી બ્રાન્ચ દીઠ ચોક્કસ એક version bump દર્શાવે.

કરવા જેવું અને ન કરવા જેવું

કરવા જેવું

  1. feat/data/fix/infra બ્રાન્ચને squash-merge કરો — WIP commits ને trunk પર એક bisectable ફેરફારમાં સંકોચે છે, જેથી eval regressions એક જ prompt અથવા data delta તરફ નિર્દેશ કરે.
  2. merge commits ફક્ત deps/* બ્રાન્ચ માટે જ મંજૂર કરો — Renovate અને Dependabot commit messages માં structured changelog metadata એમ્બેડ કરે છે જેને audit tooling પાર્સ કરે છે; squash કરવાથી તે નષ્ટ થાય છે.
  3. બ્રાન્ચ દીઠ ચોક્કસ એક prompt version બમ્પ કરો — prompt_registry.json conflicts ને સરળ રાખે છે અને trunk પર git log --oneline ને ચોક્કસ change history બનાવે છે.

ન કરવા જેવું

  1. કોઈ બ્રાન્ચને 48 કલાકથી વધુ જીવવા ન દો — JSONL appends અને prompt ફેરફારો એવો drift એકઠો કરે છે જેને Git આપમેળે ઉકેલી શકતું નથી; rebase કરીને merge કરો, અથવા બ્રાન્ચ કાઢી નાખો.
  2. શેર કરેલી બ્રાન્ચ પર force-push ન કરો — ટીમના સાથીઓ હેઠળ history ફરીથી લખવાથી તેમના rebases તૂટે છે અને trunk-based ડેવલપમેન્ટ જેના પર નિર્ભર છે તે audit trail નષ્ટ થાય છે.
  3. શેર કરેલી બ્રાન્ચ પર --no-verify વડે pre-push hook બાયપાસ ન કરો — hook એ local enforcement layer છે; તેને છોડી દો તો જૂની અથવા pattern બહારની બ્રાન્ચ remote સુધી પહોંચે છે, અને server-side branch protection એકમાત્ર બચાવ બની રહે છે.

3 hands-on labs come with this lesson — real code, in a cloud IDE. Create a free account to run them. No card.

Free account · no card · straight to the labs

Or get the full path — from

Listen to this lesson

Audio overviews of this lesson's labs and its chapter, from GenBodha Bytes.

More free lessons in DevOps Foundations for GenAI Engineers

All free lessons in LLMOps Engineering →