من خیلی جاها که برای پیادهسازی کانسپت اجایل و چارچوبهایی مثل اسکرام و کانبان، یا به صورت کلی فرایندهای اجایل حضور داشتم، همیشه بحث استیمیت و لاگ تایم زدن و بحثهای همیشگی نحوهی تخمین دادن و تکنیکهاش بوده؛ و الان هم توی کامیونیتیهای اجایل همیشه این بحث باز و داغه. اما اگه سؤال واقعیتر توی سازمان یا اون تیم این بوده که چقدر به گرفتن دیتا از توسعهدهندهها و تیملیدهاشون متعهدیم، یا بهتر بگم چقدر میشه انتظار داشت که افراد تیم دیتاهای آپدیتشده مثل لاگ تایم، میزان پیشرفت کارها و استیمیشنها رو وارد کنن تا واقعاً به سمت تحلیل Evidence-Based اسکوپ کاری بریم، چی؟ و اگه از مسائل قبلی عبور کرده باشیم، چی؟
من بعد از مدتها با تیمی در زمینهی اجایل و پیادهسازی جیرا همکاری کردم که این موضوع خیلی جدی توش دنبال میشد و تمامی پرفورمنس تیم بر اساس دیتای واردشده در ابزار سنجیده میشد. بعد از حدود ۶ ماه به صورت یک کالچر غالب دراومده بود و تمامی اون صحبتهایی که توی خیلی از تیمها میشنیدم که «چقدر وقت ما رو با دیتا وارد کردن میگیرن» از بین رفته بود. من نشانهای از قهری بودن دیتا اینتری نمیدیدم و همگی به درک درستی از Visual بودن دیتا رسیده بودن.
اما مشکل وقتی جدی میشه که میخوای از این دادهها برای مدیریت مبتنی بر شواهد (Evidence-Based Management) استفاده کنی.
من توی یک پروژهی واقعی با چند محصول و چند تیم فنی این موضوع رو جدی دنبال کردم. بعد از حدود ۶ ماه، چیزی که اول به نظر میرسید «بار اضافی» باشه، تبدیل شد به یک فرهنگ غالب تیمی. هیچکس حس نمیکرد دیتا اینتری اجبار یا قهره، بلکه همه فهمیده بودن که:
ویژوال بودن دیتا = شفافیت و تصمیمگیری بهتر
۳ چالش اصلی
۱. ساختار (Structure)
ما ۴ محصول و ۵ تیم داشتیم. اگر برای هر تیم پروژهی جدا درست میکردیم، خیلی زود به آشفتگی میرسیدیم. راهحل:
- هر محصول = یک پروژه
- هر تیم = یک برد اسکرام + یک برد کانبان
- یک Custom Field به اسم Team: استوری فقط یکبار در پروژهی محصول خودش ثبت میشد و با انتخاب تیم، همون استوری روی برد تیم مربوطه هم ظاهر میشد.
۲. دیتا اینتری (Data Entry)
اینجا نقطهی عطف ما بود. سه لایه داده تعریف کردیم:
- تخمین با Story Point
- ثبت روزانهی Log Time
- ثبت Manual Progress توسط تیملید
این سه لایه باعث شد بتونیم پیشرفت واقعی رو با پیشبینیها مقایسه کنیم.
۳. محاسبات و Roadmap Metrics
ما علاوه بر Sprint، یک مفهوم به اسم Roadmap تعریف کردیم: بازههای زمانی بلندتر (مثلاً یک فصل) که روی Epicهای چندتیمی متمرکز میشد. اینطوری علاوه بر مدیریت کوتاهمدت اسپرینت، تصویر کلان سهماهه هم داشتیم.
محاسبات سفارشی با ScriptRunner
برای این کار هیچ پلاگین آمادهای مثل BigPicture یا Advanced Roadmaps استفاده نکردیم. همهچیز با اسکریپتنویسی ScriptRunner روی خود Jira Issue ساخته شد.
ما پروژهای مجزا داشتیم که توش میتونستیم رودمپ مربوط به هر بازه و تیم رو بسازیم، با استفاده از دیتاهای کاستومشدهی مربوط به همون تیم؛ در حقیقت بازهی زمانی و ظرفیت تیم. بعد محاسبات به صورت اتومات انجام میشد.
محاسبهی ظرفیت و Availability تیمها
یکی از ارزشمندترین بخشها برای ما این بود که بتونیم ظرفیت واقعی تیمها رو هم بر اساس لاگ تایم و دادههای استوری پوینت اندازه بگیریم. برای همین سه متریک جدید تعریف کردیم:
- Roadmap Efficient Times: 623.896 ← زمان کارآمدی واقعی بر اساس لاگ تایم و پیشرفت کار.
- Roadmap Optimistic Times: 748.675 ← ظرفیت خوشبینانه (اگر همهی افراد با حداکثر توان کار کنن).
- Roadmap Pessimistic Times: 539.91 ← ظرفیت بدبینانه (اگر وقفهها و کارهای غیرمنتظره زیاد باشه).
با این سه شاخص میشد خیلی ساده دید:
- اگر با سرعت معمولی ادامه بدیم، چقدر زمان لازم داریم.
- اگر تیم در شرایط ایدهآل کار کنه، زودترین زمان اتمام چقدره.
- و اگر مشکلات زیادی پیش بیاد، چه تأخیری محتمل خواهد بود.
این دادهها به Product Ownerها و مدیرها کمک کرد که خیلی واقعبینانهتر برنامهریزی کنن. مثلاً وقتی یک Epic بزرگ به ۷۴۸ ساعت نیاز داشت ولی ظرفیت بدبینانهی تیم فقط ۵۴۰ ساعت بود، تصمیمگیری دربارهی جابهجایی اولویتها یا اضافه کردن نیرو خیلی سریعتر اتفاق میافتاد.
متریکهایی که محاسبه میکردیم
- Sum of Log: 376.72
- Roadmap Log: 242.02
- Average Manual Progress: 34.33
- Roadmap Manual Progress: 30.28
- Expected Progress: 1.56
- Real Progress: 2.578
- Sum Story Points: 153
- Logged Story Point: 125
- SP Progress: 52.525
- Logged SP Progress: 29.35
این مقادیر مستقیم روی Custom Fieldهای Issue ذخیره میشدن و بعد روی داشبورد قابل گزارشگیری بودن.
نمونه اسکریپت (Story Point Progress + Manual Progress Average)
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.MutableIssue
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.web.bean.PagerFilter
def cfm = ComponentAccessor.customFieldManager
def searchService = ComponentAccessor.getComponent(SearchService)
def user = ComponentAccessor.jiraAuthenticationContext.loggedInUser
final String SP_CF_NAME = "Story Points"
final String MANUAL_PROGRESS_CF = "Manual Progress"
final String OUTPUT_AVG_CF_NAME = "Average Manual Progress"
final String OUTPUT_SP_PROGRESS_CF_NAME = "SP Progress"
def cfSP = cfm.getCustomFieldObjectByName(SP_CF_NAME)
def cfManual = cfm.getCustomFieldObjectByName(MANUAL_PROGRESS_CF)
def cfOutputAvg = cfm.getCustomFieldObjectByName(OUTPUT_AVG_CF_NAME)
def cfOutputSP = cfm.getCustomFieldObjectByName(OUTPUT_SP_PROGRESS_CF_NAME)
def issue = issue as MutableIssue
def jql = "project = ${issue.projectObject.key} AND 'Roadmap Num' ~ '${issue.getCustomFieldValue(cfm.getCustomFieldObjectByName("Roadmap Num"))}'"
def parseResult = searchService.parseQuery(user, jql)
def searchResult = searchService.search(user, parseResult.query, PagerFilter.getUnlimitedFilter())
def issues = searchResult.issues
double totalSP = 0
double loggedSP = 0
double totalManual = 0
int countManual = 0
issues.each { i ->
def sp = i.getCustomFieldValue(cfSP) as Double ?: 0
totalSP += sp
if ((i.getTimeSpent() ?: 0) > 0) loggedSP += sp
def mp = i.getCustomFieldValue(cfManual) as Double ?: 0
if (mp > 0) {
totalManual += mp
countManual++
}
}
def avgManual = countManual > 0 ? (totalManual / countManual) : 0
def spProgress = totalSP > 0 ? (loggedSP / totalSP) * 100 : 0
issue.setCustomFieldValue(cfOutputAvg, avgManual)
issue.setCustomFieldValue(cfOutputSP, spProgress)
مثال واقعی: توسعهی API پرداخت
یک Epic در محصول B داشتیم: توسعهی سرویس پرداخت.
- Backend Team: پیادهسازی endpoint
/api/v2/payment/transaction(یک POST با JSON شامل token، amount و callback). - Mobile Team: مصرف API در اپ اندروید، طراحی UI موفق/ناموفق و هندلینگ خطا.
قبلاً این یعنی چندین تیکت جدا در پروژههای مختلف. ولی اینجا با ساختار جدید: یک استوری در پروژهی محصول B + Sub-task با Team=Backend و Team=Mobile.
نتیجه:
- PO محصول B یک فیچر کامل رو end-to-end میدید.
- تیم موبایل (که روی همهی محصولات سرویس میداد) فقط برد خودش رو نگاه میکرد و همهی کارها جمع بود.
- داشبورد Roadmap دقیقاً درصد پیشرفت واقعی در برابر انتظار رو نشون میداد.
داشبوردهای تحلیلی با Rich Filter
آخرین بخش کار ما Visualization بود. فقط محاسبات کافی نبود؛ باید همه بتونن ببینن و تحلیل کنن.
چرا Rich Filter؟
- فیلترهای دینامیک: Roadmap Num، Sprint، Priority، Project، Status.
- امکان Drill Down روی هر بُعد.
- ویجتهای متنوع برای مقایسهی Story Point و زمان صرفشده.
نمونه داشبورد (Agent Detail Performance)
- Story Point per Assignee: سهم هر فرد از Story Pointها. کمک کرد بار کاری متعادلتر بشه.
- Time Spent per Assignee: نشون داد چه کسی واقعاً وقت گذاشته. جلوی mismatch بین تخمین و لاگ گرفته شد.
- Story Point per Project: سهم تیمها بین محصولات مختلف مشخص شد. POها دیدن چه مقدار از ظرفیتشون روی کدوم پروژه مصرف میشه.
- فیلترهای سریع: امکان بررسی فقط High Priority، یا فقط Sprint جاری، یا فقط یک Roadmap خاص.
نتیجه این شد که دادهها از سطح «گزارش به مدیریت» بالاتر رفت و تبدیل شد به ابزاری برای تصمیمگیری روزانه و مدیریت بار کاری تیم.
نتیجهگیری
بعد از ۶ ماه، این مدل برای ما یک Best Practice واقعی شد:
- Product-centric projects
- Team-centric boards
- Scripted Metrics به جای پلاگینهای سنگین
- Rich Filter Dashboards برای تحلیل
و از همه مهمتر: تغییر فرهنگ تیمی. دیتا اینتری از یک کار خستهکننده تبدیل شد به ابزاری برای شفافیت و مدیریت. هم تیمها راضی بودن، هم POها تصویر کلان داشتن، هم لیدها دادهی واقعی برای تصمیمگیری داشتن.
حالا سؤال من برای شما: توی تیمتون چقدر به دادهی واقعی متعهدید؟ و آیا حاضرید به جای بحثهای بیپایان تخمین، یک بار برای همیشه ساختار و فرهنگ دیتا اینتری رو درست کنید؟
این مقاله اولین بار در ویرگول منتشر شده است.
