ট্রাংক-বেসড ডেভেলপমেন্ট ও ফিচার ফ্ল্যাগ
এই পাঠে যা শিখবেন
- ট্রাংক-বেসড ডেভেলপমেন্ট L41-এর Git Flow থেকে ঠিক কীভাবে আলাদা এবং কেন এটি হালকা
- ঘন ঘন ছোট ইন্টিগ্রেশন কীভাবে বাস্তবে বড়, বেদনাদায়ক মার্জ কনফ্লিক্ট এড়ায়
- "সরাসরি trunk-এ কমিট" মডেলের আসল সমস্যা — অসম্পূর্ণ কাজ প্রোডাকশনে যাওয়া — ও তার সমাধান
- ফিচার ফ্ল্যাগ, ডিপ্লয়/রিলিজ পার্থক্য, এবং পার্সেন্টেজ-বেসড রোলআউট প্রয়োগ করে একটি বাস্তব
FeatureFlagManager
১ · ট্রাংক-বেসড ডেভেলপমেন্ট কী
ট্রাংক-বেসড ডেভেলপমেন্টTrunk-Based Developmentএকটি হালকা-ওজনের ব্রাঞ্চিং স্ট্র্যাটেজি যেখানে সব ডেভেলপার ঘন ঘন সরাসরি একটি শেয়ার্ড ব্রাঞ্চে কমিট করে, দীর্ঘস্থায়ী ফিচার ব্রাঞ্চ ছাড়াই।
হলো L41-এর Git Flow-এর সরাসরি বিপরীত এক দর্শন। Git Flow-এ আমরা দেখেছি পাঁচটি নির্দিষ্ট ধরনের ব্রাঞ্চ
(main, develop, feature/*, release/*,
hotfix/*) — একটি কাঠামোবদ্ধ, তুলনামূলক ভারী মডেল। ট্রাংক-বেসড ডেভেলপমেন্টে এই কাঠামো নেই — সব
ডেভেলপার প্রায়ই দিনে একাধিকবার সরাসরি একটি শেয়ার্ড "trunk" ব্রাঞ্চে (সাধারণত main) কমিট করে।
ফিচার ব্রাঞ্চ ব্যবহার করা হলেও তা মাত্র কয়েক ঘণ্টার জন্য বেঁচে থাকে — দিন বা সপ্তাহের জন্য নয় — তারপরই আবার
trunk-এ মার্জ হয়ে যায়।
২ · কেন এটি কাজ করে — L36-এর সাথে সরাসরি সম্পর্ক
এই পদ্ধতির মূল যুক্তি সরাসরি M8/L36-এর মার্জ কনফ্লিক্ট আলোচনার সাথে যুক্ত। মনে করুন, একটি মার্জ কনফ্লিক্ট ঘটে যখন দুটো ব্রাঞ্চ একই লাইনে ভিন্নভাবে পরিবর্তন করে — এবং একটি ব্রাঞ্চ যত বেশি দিন ধরে trunk থেকে বিচ্যুত থাকে, তত বেশি পরিবর্তন জমা হয়, ফলে কনফ্লিক্টের সম্ভাবনা ও তীব্রতা দুটোই বাড়ে। ট্রাংক-বেসড ডেভেলপমেন্ট এই সমস্যাটির মূলেই আঘাত করে — প্রতিটি ইন্টিগ্রেশন ছোট ও ঘন ঘন হওয়ায়, কোনো ব্রাঞ্চ কখনোই বেশিদূর বিচ্যুত হওয়ার সুযোগ পায় না, ফলে প্রতিটি মার্জ সাধারণত ছোট, সহজ ও কম-ঝুঁকিপূর্ণ থাকে।
কোনোটিই সার্বজনীনভাবে "ভালো" নয় — L41-এ যেমন বলা হয়েছিল, Git Flow নির্ধারিত, ভার্সন-নম্বরযুক্ত রিলিজ থাকা প্রজেক্টের জন্য ভালো ফিট করে। ট্রাংক-বেসড ডেভেলপমেন্ট সেই দলের জন্য বেশি উপযোগী যারা কন্টিনিউয়াস ডিপ্লয়মেন্ট করে (M12/L54-এ বিস্তারিত) — যেখানে কোড দিনে বহুবার প্রোডাকশনে যায়, এবং Git Flow-এর একাধিক দীর্ঘস্থায়ী ব্রাঞ্চ রক্ষণাবেক্ষণ করার ওভারহেড অপ্রয়োজনীয় বোঝা হয়ে দাঁড়ায়।
৩ · আসল সমস্যা — অসম্পূর্ণ ফিচার প্রোডাকশনে যাওয়া
কিন্তু এখানে একটি বাস্তব, গুরুত্বপূর্ণ সমস্যা তৈরি হয়: যদি সবাই ঘন ঘন সরাসরি trunk-এ কমিট করে, তাহলে
অসম্পূর্ণ, এখনো-টেস্ট-না-হওয়া ফিচারের কোড কীভাবে প্রোডাকশনে পাঠানো থেকে ঠেকানো যায়? Git Flow-এ
feature/* ব্রাঞ্চ ফিচার সম্পূর্ণ না হওয়া পর্যন্ত develop-এ যাওয়া থেকে বিরত রাখে —
কিন্তু ট্রাংক-বেসড ডেভেলপমেন্টে সেই সুরক্ষা নেই, কারণ কোড প্রায় সাথে সাথেই trunk-এ চলে যায়।
৪ · সমাধান — ফিচার ফ্ল্যাগ
ফিচার ফ্ল্যাগFeature Flag / Feature Toggleকোডের একটি কন্ডিশনাল চেক (যেমন if flag_is_enabled(...)) যা অসম্পূর্ণ ফাংশনালিটি নিরাপদে প্রোডাকশনে ডিপ্লয় করতে দেয়, বাস্তব ইউজারের কাছে তা সক্রিয় না করেই।
(aka ফিচার টগল) হলো এই সমস্যার প্রমিত সমাধান — কোডে একটি সাধারণ কন্ডিশনাল চেক থাকে, যেমন:
if feature_flags.is_enabled("new_checkout_flow", user_id): ... নতুন কোড ...
else: ... পুরনো, স্থিতিশীল কোড ...
এভাবে নতুন, এখনো-অসম্পূর্ণ কোড কমিট এবং এমনকি প্রোডাকশনে ডিপ্লয় করা যায়, কিন্তু ফ্ল্যাগ চালু না করা পর্যন্ত বাস্তব ইউজারের কাছে তা সম্পূর্ণ নিষ্ক্রিয়/অদৃশ্য থাকে। এটি জেনুইনভাবে ডিপ্লয় করা (কোড প্রোডাকশন সার্ভারে পাঠানো) এবং রিলিজ করা (ফিচার বাস্তব ইউজারের কাছে সক্রিয় করা) — এই দুটো কাজকে সম্পূর্ণ আলাদা করে দেয়। ফ্ল্যাগ প্রায়ই ধীরে ধীরে বাড়ানো হয় — প্রথমে অভ্যন্তরীণ টেস্ট অ্যাকাউন্টের জন্য, তারপর ইউজারদের একটি ছোট শতাংশের জন্য, ধীরে ধীরে ১০০% পর্যন্ত — একে "পার্সেন্টেজ রোলআউট" বলা হয়।
একটি ফিচার সম্পূর্ণভাবে সবার জন্য চালু বা বন্ধ — সবচেয়ে সরল রূপ, সাধারণত ডেভেলপমেন্টের প্রথম ধাপে ব্যবহৃত।
ইউজারদের একটি নির্দিষ্ট, ধারাবাহিক (deterministic) শতাংশের জন্য ফিচার চালু — ধীরে ধীরে ঝুঁকি কমিয়ে বাড়ানো হয়।
নিচের কোড সেলে আমরা একটি বাস্তব FeatureFlagManager ইমপ্লিমেন্ট করব যা উভয় ধরনের ফ্ল্যাগ
সাপোর্ট করে — সাধারণ অন/অফ, এবং পার্সেন্টেজ রোলআউট। পার্সেন্টেজ রোলআউট বাস্তবায়নের জন্য প্রতিটি ইউজারের জন্য
একটি ধারাবাহিক (deterministic) "বাকেট" দরকার — একই ইউজার সবসময় একই বাকেটে পড়বে, যাতে একজন
ইউজার একবার ফিচার দেখলে পরের বার হঠাৎ সেটি অদৃশ্য হয়ে না যায়। Python-এর নিজস্ব hash() ফাংশন
প্রতিটি রান-এ ভিন্ন ফলাফল দেয় (randomized) — তাই আমরা একটি সরল, নিজস্ব ধারাবাহিক হ্যাশ ফাংশন লিখব।
# একটি ফিচার ফ্ল্যাগ ম্যানেজার -- পার্সেন্টেজ রোলআউট সাপোর্ট সহ।
# নোট: Python-এর বিল্ট-ইন hash() প্রতি রান-এ randomized, তাই reproducible রেজাল্টের জন্য
# আমরা নিজেদের সরল, ধারাবাহিক (deterministic) হ্যাশ ফাংশন লিখছি।
def simple_deterministic_hash(text):
h = 5381
for ch in text:
h = (h * 33 + ord(ch)) % 1000003
return h
class FeatureFlagManager:
def __init__(self):
self.flags = {} # flag_name -> rollout_percentage (0-100)
def set_flag(self, flag_name, rollout_percentage):
self.flags[flag_name] = rollout_percentage
def is_enabled(self, flag_name, user_id):
rollout_percentage = self.flags.get(flag_name, 0)
if rollout_percentage <= 0:
return False
if rollout_percentage >= 100:
return True
bucket = simple_deterministic_hash(f"{flag_name}:{user_id}") % 100
return bucket < rollout_percentage
manager = FeatureFlagManager()
sample_users = [f"user{i}" for i in range(2000)]
# ০% রোলআউট -- কেউ দেখে না
manager.set_flag("new_checkout_flow", 0)
count_0 = sum(1 for u in sample_users if manager.is_enabled("new_checkout_flow", u))
print(f"0% রোলআউট -- {count_0}/{len(sample_users)} ইউজার ফিচারটি দেখছে")
# ৫০% রোলআউট -- আনুমানিক অর্ধেক ইউজার দেখবে
manager.set_flag("new_checkout_flow", 50)
count_50 = sum(1 for u in sample_users if manager.is_enabled("new_checkout_flow", u))
pct_50 = count_50 / len(sample_users) * 100
print(f"50% রোলআউট -- {count_50}/{len(sample_users)} ইউজার দেখছে ({pct_50:.1f}%)")
# 100% রোলআউট -- সবাই দেখে
manager.set_flag("new_checkout_flow", 100)
count_100 = sum(1 for u in sample_users if manager.is_enabled("new_checkout_flow", u))
print(f"100% রোলআউট -- {count_100}/{len(sample_users)} ইউজার দেখছে")
simple_deterministic_hash ইউজার আইডিগুলোকে
১০০টি বাকেটে মোটামুটি সমানভাবে ছড়িয়ে দেয়, কিন্তু নিখুঁত ৫০/৫০ বিভাজনের কোনো গ্যারান্টি নেই — ঠিক যেমন একটি
বাস্তব কয়েন ২০০০ বার টস করলে ঠিক ১০০০ বার হেড আসার নিশ্চয়তা থাকে না, শুধু তার কাছাকাছি থাকার সম্ভাবনা বেশি।
ট্রাংক-বেসড ডেভেলপমেন্ট ঘন ঘন, ছোট ইন্টিগ্রেশনের মাধ্যমে L36-এর বড় মার্জ কনফ্লিক্টের ঝুঁকি কমায়, কিন্তু এর জন্য একটি ভিন্ন সুরক্ষা ব্যবস্থা দরকার — ফিচার ফ্ল্যাগ সেই ভূমিকা পালন করে, কোড ডিপ্লয় করা আর ফিচার রিলিজ করাকে আলাদা করে দিয়ে, এবং পার্সেন্টেজ রোলআউটের মাধ্যমে ধাপে ধাপে, নিয়ন্ত্রিতভাবে ঝুঁকি নেওয়ার সুযোগ দিয়ে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ একটি দল যদি Git Flow থেকে ট্রাংক-বেসড ডেভেলপমেন্টে স্থানান্তরিত হয়, কিন্তু ফিচার ফ্ল্যাগ ব্যবহার না করে, তাহলে সবচেয়ে বড় ঝুঁকি কী?
সবচেয়ে বড় ঝুঁকি হলো অসম্পূর্ণ, এখনো-টেস্ট-না-হওয়া কোড সরাসরি trunk-এ, এবং সেখান থেকে প্রোডাকশনে চলে
যাওয়া — Git Flow-এ feature/* ব্রাঞ্চ এই সুরক্ষা দিত (অসম্পূর্ণ কাজ develop-এ
মার্জ না হওয়া পর্যন্ত আলাদা থাকত), কিন্তু ট্রাংক-বেসড মডেলে সেই সুরক্ষা স্তর নেই। ফিচার ফ্ল্যাগ ছাড়া
দলটি হয় খুব ধীরে কমিট করবে (ট্রাংক-বেসডের মূল সুবিধাই হারিয়ে ফেলবে), অথবা বাস্তবে অসম্পূর্ণ ফিচার
ইউজারদের সামনে চলে যাওয়ার ঝুঁকি নেবে।
প্র ০২
উপরের কোড সেলে simple_deterministic_hash-এর বদলে যদি Python-এর সাধারণ hash() ফাংশন ব্যবহার করা হতো, তাহলে কী সমস্যা হতো?
Python-এর বিল্ট-ইন hash() স্ট্রিং-এর জন্য প্রতিটি প্রোগ্রাম রান-এ ভিন্ন ফলাফল দেয়
(একটি র্যান্ডম সল্ট ব্যবহার করে, নিরাপত্তার কারণে) — অর্থাৎ একই ইউজার আজ ফিচারটি দেখতে পেলেও, আগামীকাল
প্রোগ্রাম আবার চালু হলে (বা সার্ভার রিস্টার্ট হলে) সেই একই ইউজার হঠাৎ ফিচারটি না-ও দেখতে পারে। এটি একটি
বাস্তব রোলআউটের জন্য সম্পূর্ণ অগ্রহণযোগ্য — ইউজারের অভিজ্ঞতা ধারাবাহিক (consistent) থাকা জরুরি, তাই
আমাদের একটি নিজস্ব, ধারাবাহিক হ্যাশ ফাংশন দরকার হয়েছিল।
প্র ০৩ "ডিপ্লয় করা" এবং "রিলিজ করা" — এই দুটো আলাদা করার আসল ব্যবহারিক সুবিধা কী?
এই বিচ্ছেদ একটি দলকে কোড পাঠানো (একটি প্রায়ই স্বয়ংক্রিয়, কম-ঝুঁকিপূর্ণ প্রযুক্তিগত কাজ) এবং ফিচার প্রকাশ করা (একটি ব্যবসায়িক/প্রোডাক্ট সিদ্ধান্ত, প্রায়ই নির্দিষ্ট সময়ে বা নির্দিষ্ট শর্তে করতে চাওয়া হয়) — এই দুটোকে স্বাধীনভাবে নিয়ন্ত্রণ করতে দেয়। কোড অনেক আগেই প্রোডাকশনে "মোতায়েন" হয়ে বসে থাকতে পারে (ফ্ল্যাগ বন্ধ অবস্থায়), এবং একটি সমস্যা ধরা পড়লে ফিচারটি তাৎক্ষণিকভাবে বন্ধ করে দেওয়া যায় (নতুন কোড ডিপ্লয় না করেই, শুধু ফ্ল্যাগ টগল করে) — এটি M12-এর কন্টিনিউয়াস ডেলিভারির একটি গুরুত্বপূর্ণ ব্যবহারিক হাতিয়ার।
অনুশীলন
-
চিন্তা করুন: একটি ইকমার্স সাইটে একটি নতুন "রিকমেন্ডেশন ইঞ্জিন" ফিচার প্রথমে মাত্র ৫% ইউজারের জন্য চালু করা হলো। দুই দিন পর মনিটরিং-এ দেখা গেল সেই ৫% ইউজারের মধ্যে এরর রেট স্বাভাবিকের চেয়ে বেশি। ফিচার ফ্ল্যাগ ছাড়া এই পরিস্থিতি সামলাতে কী করতে হতো, আর ফ্ল্যাগ থাকলে কী করা যায়?
ফিচার ফ্ল্যাগ ছাড়া, সমস্যাযুক্ত কোড সরাসরি প্রোডাকশনে থাকলে দলটিকে একটি নতুন "রিভার্ট" কমিট (M7/L33 রিভিউ করুন) তৈরি করে আবার ডিপ্লয় করতে হতো — যা সময়সাপেক্ষ এবং নতুন ঝুঁকি যোগ করতে পারে। ফিচার ফ্ল্যাগ থাকলে, সমাধান তাৎক্ষণিক: শুধু
set_flag("recommendation_engine", 0)কল করলেই ফিচারটি সব ইউজারের জন্য সাথে সাথে বন্ধ হয়ে যায় — কোনো নতুন কোড ডিপ্লয়মেন্ট ছাড়াই, এবং কোডটি ডিবাগ করার জন্য এখনো প্রোডাকশনে (কিন্তু নিষ্ক্রিয়) থেকে যায়। -
পরীক্ষা করুন: উপরের কোড সেলে
sample_users-এর সংখ্যা ২০০০ থেকে কমিয়ে মাত্র ২০ করে দিন এবং ৫০% রোলআউট আবার চালান। পর্যবেক্ষিত শতাংশ ৫০%-এর কতটা কাছাকাছি থাকে, আগের চেয়ে বেশি না কম বিচ্যুত?ছোট নমুনা (২০ জন) সাধারণত ৫০%-এর চেয়ে বেশি বিচ্যুত ফলাফল দেবে (যেমন ৩৫% বা ৬০%) — কারণ পরিসংখ্যানের সাধারণ নিয়ম অনুযায়ী নমুনার আকার যত ছোট, দৈবচয়নের (এখানে হ্যাশ-বাকেটিং-এর) ওঠানামা তত বেশি লক্ষণীয় হয়। ২০০০ জনের বড় নমুনায় এই ওঠানামা গড়ে অনেক কম দেখা যায় — এটিই বাস্তব রোলআউটেও সত্য: খুব ছোট ইউজার-বেস থাকা একটি ফিচারে "৫০% রোলআউট" ঠিক ৫০% এর কাছাকাছি নাও দেখাতে পারে।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ পরের পাঠ — Git ট্যাগ, রিলিজ ও সিমান্টিক ভার্সনিং — দেখাবে কীভাবে একটি নির্দিষ্ট কমিটকে স্থায়ীভাবে একটি রিলিজ হিসেবে চিহ্নিত করা হয়।
- Git Flow ব্রাঞ্চিং স্ট্র্যাটেজি (L41) M9 · আগের পাঠ এই পাঠের ট্রাংক-বেসড দর্শন সরাসরি L41-এর Git Flow-এর কাঠামোবদ্ধ মডেলের বিপরীতে তুলনা করে বোঝা হয়েছে।
- Cloud Computing & DevOps কোর্স সঙ্গী কোর্স ফিচার ফ্ল্যাগ ও কন্টিনিউয়াস ডেলিভারির আসল বাস্তবায়ন (ডিপ্লয়মেন্ট পাইপলাইন, ইনফ্রাস্ট্রাকচার) সেই কোর্সে বিস্তারিত।