ইউজার স্টোরি ও অ্যাকসেপ্টেন্স ক্রাইটেরিয়া
এই পাঠে যা শিখবেন
- ইউজার স্টোরি কী এবং এটি L12-এর ইউজ কেস থেকে কীভাবে আলাদা
- স্ট্যান্ডার্ড "As a... I want... so that..." টেমপ্লেট ও 3 Cs মেমোনিক
- অ্যাকসেপ্টেন্স ক্রাইটেরিয়া কী এবং কেন তা টেস্টযোগ্য হওয়া জরুরি
- INVEST মানদণ্ড — একটি ভালো ইউজার স্টোরির বৈশিষ্ট্য
- Python দিয়ে দুটি ইউজার স্টোরি তৈরি করে, একটির অ্যাকসেপ্টেন্স ক্রাইটেরিয়া টেস্টযোগ্য এবং অন্যটির অস্পষ্ট — এবং একটি চেকার দিয়ে পার্থক্য শনাক্ত করা
১ · ইউজার স্টোরি কী — L12-এর সাথে পার্থক্য
ইউজার স্টোরিUser Storyএকজন শেষ-ব্যবহারকারীর দৃষ্টিকোণ থেকে একটি রিকোয়ারমেন্ট ধরার একটি হালকা, অনানুষ্ঠানিক পদ্ধতি। হলো একজন শেষ-ব্যবহারকারীর দৃষ্টিকোণ থেকে রিকোয়ারমেন্ট ধরার একটি হালকা, অনানুষ্ঠানিক পদ্ধতি। L12-এর ইউজ কেসের সাথে সরাসরি তুলনা করলে পার্থক্যটা স্পষ্ট হয়: একটি ইউজ কেস ইচ্ছাকৃতভাবে বিস্তারিত ও সম্পূর্ণ (প্রিকন্ডিশন, পূর্ণ মেইন ফ্লো, সব অল্টারনেটিভ ফ্লো), যেখানে একটি ইউজার স্টোরি ইচ্ছাকৃতভাবে ডিটেইলের বদলে গতি বেছে নেয় — M2/L08-এর অ্যাজাইল দর্শনের একটি স্বাভাবিক সহযোগী।
২ · স্ট্যান্ডার্ড টেমপ্লেট ও 3 Cs
একটি ইউজার স্টোরির ক্লাসিক ফরম্যাট: "As a [ধরনের ইউজার], I want [একটি লক্ষ্য], so that [একটি কারণ/সুবিধা]"। শেষ অংশ — "so that" — প্রায়ই উপেক্ষিত হয়, কিন্তু আসলে গুরুত্বপূর্ণ: এটি আসল প্রয়োজনটা ধরে রাখে (L10-এর "রিকোয়ারমেন্ট গ্যাপ" আলোচনার সরাসরি প্রতিধ্বনি) — "কেন" জানা থাকলে টিম ইমপ্লিমেন্টেশনের যে ডিটেইলগুলো সুনির্দিষ্টভাবে বলা নেই, সেগুলোতে ভালো সিদ্ধান্ত নিতে পারে।
স্টোরিটি একটি ছোট কার্ডে (ফিজিক্যাল বা ডিজিটাল) সংক্ষেপে লেখা থাকে — পুরো ডিটেইল নয়।
ডিটেইল আগে থেকে সম্পূর্ণভাবে লেখার বদলে চলমান আলোচনার মাধ্যমে বের হয়ে আসে।
অ্যাকসেপ্টেন্স ক্রাইটেরিয়া — কীভাবে নিশ্চিত করা হবে স্টোরিটি সঠিকভাবে সম্পন্ন হয়েছে।
৩ · অ্যাকসেপ্টেন্স ক্রাইটেরিয়া — টেস্টযোগ্যতার সেতু
অ্যাকসেপ্টেন্স ক্রাইটেরিয়াAcceptance Criteriaএকটি নির্দিষ্ট, টেস্টযোগ্য শর্তের তালিকা, যা একটি ইউজার স্টোরিকে "সম্পন্ন" ঘোষণা করার জন্য অবশ্যই সত্য হতে হবে। হলো একটি নির্দিষ্ট, টেস্টযোগ্য শর্তের তালিকা, যা একটি স্টোরিকে "সম্পন্ন" ঘোষণা করার জন্য অবশ্যই সত্য হতে হবে। এটি একটি হালকা ইউজার স্টোরিকে L11-এর পরিমাপযোগ্য রিকোয়ারমেন্টের প্রয়োজনের সাথে সংযুক্ত করে — এবং M10-এর টেস্টিং মডিউলের সরাসরি ভিত্তি স্থাপন করে: অ্যাকসেপ্টেন্স ক্রাইটেরিয়া প্রায়ই সরাসরি আসল অ্যাকসেপ্টেন্স টেস্ট কেসে রূপান্তরিত হয়।
৪ · INVEST মানদণ্ড
একটি ভালো ইউজার স্টোরির বৈশিষ্ট্য বর্ণনা করার জন্য একটি সুপরিচিত মেমোনিক:
অন্য স্টোরির উপর অতিরিক্ত নির্ভরশীল না হওয়া।
একটি চুক্তি নয়, বরং আলোচনার সুযোগ থাকা।
ব্যবহারকারীর জন্য প্রকৃত মূল্য প্রদান করা।
টিম মোটামুটি আন্দাজ করতে পারা কতটা কাজ লাগবে।
একটি স্প্রিন্টে সম্পন্ন করার মতো যথেষ্ট ছোট।
স্পষ্ট অ্যাকসেপ্টেন্স ক্রাইটেরিয়া থাকা, যা যাচাই করা যায়।
৫ · কোড দিয়ে — একটি ভালো স্টোরি বনাম একটি অস্পষ্ট স্টোরি
নিচে দুটি ইউজার স্টোরি তৈরি করা হলো — একটির অ্যাকসেপ্টেন্স ক্রাইটেরিয়া প্রতিটিতে একটি নির্দিষ্ট, যাচাইযোগ্য
সংখ্যা/শর্ত আছে (L11-এর measurability-check-এর একই চেতনা পুনরায় ব্যবহার করে), অন্যটির ক্রাইটেরিয়া ইচ্ছাকৃতভাবে
অস্পষ্ট। একটি সাধারণ is_testable() চেকার দিয়ে দেখা যাক এটি সত্যিই পার্থক্যটা ধরতে পারে কিনা।
class UserStory:
def __init__(self, role, goal, reason, acceptance_criteria):
self.role = role
self.goal = goal
self.reason = reason
self.acceptance_criteria = acceptance_criteria
def format_story(self):
return f"As a {self.role}, I want {self.goal}, so that {self.reason}."
# L11-এর measurability-check-এর একই চেতনা: একটি ক্রাইটেরিয়ন টেস্টযোগ্য হলে তাতে একটি স্পষ্ট,
# যাচাইযোগ্য সংখ্যা/শর্ত থাকা উচিত -- শুধু একটি অস্পষ্ট গুণবাচক শব্দ (fast, easy, user-friendly) থাকলে নয়।
VAGUE_WORDS = ["fast", "easy", "user-friendly", "nice", "quickly", "ভালো", "সহজ"]
def is_testable(criterion_text):
has_number = any(ch.isdigit() for ch in criterion_text)
has_vague_word = any(w in criterion_text.lower() for w in VAGUE_WORDS)
return has_number and not has_vague_word
good_story = UserStory(
role="রেজিস্টার্ড কাস্টমার",
goal="আমার অর্ডার হিস্ট্রি দেখা",
reason="আমি আগের কেনাকাটার রেকর্ড রাখতে পারি",
acceptance_criteria=[
"'অর্ডার হিস্ট্রি' ট্যাব ক্লিক করলে গত ১২ মাসের সব অর্ডার তালিকা আকারে দেখা যায়",
"প্রতিটি অর্ডারে অর্ডার নম্বর, তারিখ ও মোট মূল্য -- এই ৩টি তথ্য অবশ্যই দেখানো হবে",
"তালিকাটি ৫০০ মিলিসেকেন্ডের মধ্যে লোড হবে",
],
)
vague_story = UserStory(
role="নতুন ভিজিটর",
goal="সাইটটি সহজে ব্যবহার করা",
reason="আমি বিরক্ত না হয়ে কেনাকাটা করতে পারি",
acceptance_criteria=[
"সাইটটি user-friendly এবং fast হতে হবে",
],
)
for story in (good_story, vague_story):
print(story.format_story())
for c in story.acceptance_criteria:
verdict = "টেস্টযোগ্য" if is_testable(c) else "টেস্টযোগ্য নয় (অস্পষ্ট)"
print(f" [{verdict}] {c}")
print()
good_story-এর প্রতিটি ক্রাইটেরিয়নে একটি স্পষ্ট সংখ্যা আছে (১২ মাস, ৩টি তথ্য, ৫০০
মিলিসেকেন্ড) — তাই is_testable() প্রতিটিকে "টেস্টযোগ্য" হিসেবে চিহ্নিত করে। কিন্তু
vague_story-এর ক্রাইটেরিয়নে "user-friendly" ও "fast" শব্দ দুটো থাকলেও কোনো নির্দিষ্ট সংখ্যা বা
যাচাইযোগ্য শর্ত নেই — এটি ঠিক সেই ধরনের অস্পষ্ট, প্রায়-অকেজো নন-ফাংশনাল বিবৃতি যা L11-এ আমরা "measurable
নয়" বলে চিহ্নিত করেছিলাম।
ইউজার স্টোরি ইচ্ছাকৃতভাবে হালকা — কিন্তু অ্যাকসেপ্টেন্স ক্রাইটেরিয়া সেই হালকা ফরম্যাটকেও সম্পূর্ণ যাচাইযোগ্য রাখে। "As a... I want... so that..." গতি দেয়, INVEST মানদণ্ড মান নিশ্চিত করে, আর অ্যাকসেপ্টেন্স ক্রাইটেরিয়া M10-এর টেস্টিং মডিউলের সরাসরি সেতু তৈরি করে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ "As a... I want..." লেখার পর "so that..." অংশটি বাদ দিলে বাস্তবে কী সমস্যা হতে পারে?
"so that" ছাড়া টিম শুধু জানে ইউজার কী চায়, কেন চায় তা জানে না। বাস্তবায়নের সময় যখন এমন কোনো ডিটেইল সামনে আসে যা স্টোরিতে স্পষ্টভাবে বলা নেই, তখন "কেন" না জানলে টিম ভুল অনুমান করতে পারে। উদাহরণ — "আমি অর্ডার হিস্ট্রি দেখতে চাই" এর পেছনে কারণ যদি হয় "রিটার্ন রিকোয়েস্ট করার জন্য", তাহলে ডিজাইনে রিটার্ন বাটন থাকা জরুরি, কিন্তু কারণটা না জানলে এটি সহজেই বাদ পড়ে যেতে পারে।
প্র ০২ INVEST-এর "Small" (ছোট) মানদণ্ড কেন গুরুত্বপূর্ণ — একটি বড়, বহু-সপ্তাহের স্টোরি কী সমস্যা তৈরি করতে পারে?
একটি বড় স্টোরি সঠিকভাবে estimate করা কঠিন (INVEST-এর "Estimable"-এর সাথে সরাসরি সাংঘর্ষিক), একটি স্প্রিন্টের মধ্যে সম্পন্ন করা কঠিন (M2/L09-এর স্ক্রাইন্টের ফিক্সড টাইম-বক্সের সাথে বেমানান), এবং কাজ শুরুর অনেক পরে পর্যন্ত ওয়ার্কিং সফটওয়্যার দেখা যায় না — যা M2/L05-এর ওয়াটারফলের ঠিক সেই দুর্বলতার প্রতিধ্বনি যা M2/L06-এর ইনক্রিমেন্টাল মডেল সমাধান করার চেষ্টা করেছিল।
প্র ০৩
উপরের কোড সেলে is_testable() শুধু সংখ্যা আছে কিনা দেখে সিদ্ধান্ত নেয় — এই হিউরিস্টিকের একটি সীমাবদ্ধতা কী হতে পারে?
একটি সরল উদাহরণ: "সিস্টেমে ৫ জন ডেভেলপার কাজ করে" — এই বাক্যে একটি সংখ্যা (৫) আছে, তাই
is_testable() এটিকে "টেস্টযোগ্য" বলে চিহ্নিত করবে, কিন্তু এটি আদৌ কোনো যাচাইযোগ্য
অ্যাকসেপ্টেন্স শর্তই নয় — এটি স্রেফ একটি তথ্য বাক্য। বাস্তব জগতে কীওয়ার্ড-ভিত্তিক হিউরিস্টিক সবসময়
অসম্পূর্ণ — এটি একটি প্রথম-ধাপের সংকেত মাত্র, চূড়ান্ত রায় সবসময় একজন মানুষের যাচাই দরকার।
অনুশীলন
-
চিন্তা করুন: "As a মোবাইল অ্যাপ ইউজার, I want অফলাইনেও আমার নোট দেখতে, so that ইন্টারনেট না থাকলেও কাজ চালিয়ে যেতে পারি" — এই স্টোরির জন্য ২টি টেস্টযোগ্য অ্যাকসেপ্টেন্স ক্রাইটেরিয়া লিখুন।
একটি যুক্তিসঙ্গত উত্তর: "(১) ইন্টারনেট সংযোগ বন্ধ থাকা অবস্থায় অ্যাপ খুললে সর্বশেষ সিঙ্ক করা সব নোট দেখা যায়। (২) অফলাইনে থাকা অবস্থায় একটি নোট এডিট করলে, ইন্টারনেট ফিরে আসার ৫ সেকেন্ডের মধ্যে পরিবর্তনটি স্বয়ংক্রিয়ভাবে সার্ভারে সিঙ্ক হয়।" — দুটোতেই একটি নির্দিষ্ট, যাচাইযোগ্য শর্ত আছে (কোন নোট দেখা যাবে, ঠিক কত সময়ের মধ্যে সিঙ্ক হবে) — অস্পষ্ট বিশেষণের বদলে।
-
পরীক্ষা করুন: উপরের কোড সেলে
vague_story-এর অ্যাকসেপ্টেন্স ক্রাইটেরিয়াকে "পেজটি ২ সেকেন্ডের মধ্যে লোড হবে" দিয়ে বদলে Run চেপে দেখুনis_testable()-এর রায় কীভাবে বদলায়।নতুন ক্রাইটেরিয়নে একটি সংখ্যা (২) আছে এবং কোনো
VAGUE_WORDS-এর শব্দ নেই, তাইis_testable()এখন এটিকে "টেস্টযোগ্য" হিসেবে চিহ্নিত করবে —vague_storyএখন আর "অস্পষ্ট" থাকবে না। এটি concretely দেখায় কীভাবে একটি অস্পষ্ট ক্রাইটেরিয়নকে শুধু একটি নির্দিষ্ট সংখ্যা যোগ করেই টেস্টযোগ্য করে তোলা যায়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পাঠ ১৪ · কাপলিং ও কোহেশন পরবর্তী পাঠ রিকোয়ারমেন্ট থেকে ডিজাইনে রূপান্তর শুরু — M4 মডিউলের প্রথম ডিজাইন প্রিন্সিপল।
- পাঠ ১২ · ইউজ কেস ও ইউজ কেস ডায়াগ্রাম আগের পাঠ একই রিকোয়ারমেন্টকে আরও বিস্তারিত, আনুষ্ঠানিক ফরম্যাটে লেখার পদ্ধতি।