Agile และ Scrum Framework
Agile เป็นแนวทางพัฒนาซอฟต์แวร์ที่เน้นการส่งมอบงานเป็นรอบสั้นๆ ปรับเปลี่ยนได้ตาม Feedback Scrum เป็น Framework ที่นิยมที่สุดภายใต้ Agile แบ่งงานเป็น Sprint มี Roles, Events และ Artifacts ที่ชัดเจน
Agile Manifesto มี 4 ค่านิยมหลัก ให้ความสำคัญกับ Individuals and Interactions มากกว่า Processes, Working Software มากกว่า Documentation, Customer Collaboration มากกว่า Contract Negotiation และ Responding to Change มากกว่า Following a Plan
| หัวข้อ | Waterfall | Agile/Scrum |
|---|---|---|
| การวางแผน | วางแผนทั้งหมดตั้งแต่ต้น | วางแผนเป็นรอบ Sprint |
| การส่งมอบ | ส่งมอบครั้งเดียวตอนจบ | ส่งมอบทุก Sprint |
| การเปลี่ยนแปลง | ยากและแพง | ยินดีรับการเปลี่ยนแปลง |
| Feedback | ได้ตอนจบ Project | ได้ทุก Sprint |
| ความเสี่ยง | รู้ตอนจบ | รู้เร็วแก้เร็ว |
| เหมาะกับ | Requirements ชัดเจน | Requirements เปลี่ยนได้ |
Scrum Roles, Events และ Artifacts
# scrum_framework.py — Scrum Framework Implementation
from dataclasses import dataclass, field
from typing import List, Optional
from datetime import datetime, timedelta
from enum import Enum
class Priority(Enum):
CRITICAL = 1
HIGH = 2
MEDIUM = 3
LOW = 4
class Status(Enum):
TODO = "To Do"
IN_PROGRESS = "In Progress"
IN_REVIEW = "In Review"
DONE = "Done"
@dataclass
class UserStory:
id: str
title: str
description: str
acceptance_criteria: List[str]
story_points: int
priority: Priority
status: Status = Status.TODO
assignee: str = ""
sprint: Optional[str] = None
@dataclass
class Sprint:
name: str
goal: str
start_date: datetime
duration_weeks: int = 2
stories: List[UserStory] = field(default_factory=list)
velocity: int = 0
@property
def end_date(self):
return self.start_date + timedelta(weeks=self.duration_weeks)
@property
def total_points(self):
return sum(s.story_points for s in self.stories)
@property
def completed_points(self):
return sum(s.story_points for s in self.stories if s.status == Status.DONE)
@property
def progress_pct(self):
if self.total_points == 0:
return 0
return self.completed_points / self.total_points * 100
@dataclass
class ProductBacklog:
items: List[UserStory] = field(default_factory=list)
def add(self, story: UserStory):
self.items.append(story)
self.items.sort(key=lambda s: s.priority.value)
def top(self, n=10):
return [s for s in self.items if s.status == Status.TODO][:n]
class ScrumBoard:
"""Scrum Board สำหรับจัดการ Sprint"""
def __init__(self, team_name: str, velocity: int = 30):
self.team_name = team_name
self.avg_velocity = velocity
self.backlog = ProductBacklog()
self.sprints: List[Sprint] = []
self.current_sprint: Optional[Sprint] = None
def create_story(self, title, description, criteria, points, priority):
story_id = f"US-{len(self.backlog.items)+1:04d}"
story = UserStory(
id=story_id, title=title, description=description,
acceptance_criteria=criteria, story_points=points,
priority=priority,
)
self.backlog.add(story)
return story
def sprint_planning(self, sprint_name, goal, start_date):
"""Sprint Planning — เลือก Stories สำหรับ Sprint"""
sprint = Sprint(name=sprint_name, goal=goal, start_date=start_date)
remaining = self.avg_velocity
for story in self.backlog.top(20):
if story.story_points <= remaining:
story.sprint = sprint_name
story.status = Status.TODO
sprint.stories.append(story)
remaining -= story.story_points
self.current_sprint = sprint
self.sprints.append(sprint)
print(f"\n{'='*55}")
print(f"Sprint Planning: {sprint_name}")
print(f" Goal: {goal}")
print(f" Duration: {sprint.start_date.strftime('%Y-%m-%d')} to {sprint.end_date.strftime('%Y-%m-%d')}")
print(f" Stories: {len(sprint.stories)} ({sprint.total_points} points)")
print(f" Capacity: {self.avg_velocity} points")
print(f"{'='*55}")
for story in sprint.stories:
print(f" [{story.id}] {story.title} ({story.story_points} pts) — {story.priority.name}")
def daily_scrum(self):
"""Daily Scrum — สรุปสถานะรายวัน"""
if not self.current_sprint:
return
sprint = self.current_sprint
print(f"\n Daily Scrum — {sprint.name}")
print(f" Progress: {sprint.progress_pct:.0f}% ({sprint.completed_points}/{sprint.total_points} pts)")
for status in Status:
stories = [s for s in sprint.stories if s.status == status]
if stories:
print(f"\n [{status.value}]")
for s in stories:
assignee = f" @{s.assignee}" if s.assignee else ""
print(f" {s.id}: {s.title}{assignee}")
def sprint_review(self):
"""Sprint Review — Demo และ Review"""
if not self.current_sprint:
return
sprint = self.current_sprint
done = [s for s in sprint.stories if s.status == Status.DONE]
not_done = [s for s in sprint.stories if s.status != Status.DONE]
print(f"\n{'='*55}")
print(f"Sprint Review: {sprint.name}")
print(f" Goal: {sprint.goal}")
print(f" Velocity: {sprint.completed_points} points")
print(f" Completed: {len(done)}/{len(sprint.stories)} stories")
print(f"{'='*55}")
if done:
print(f"\n Completed:")
for s in done:
print(f" [DONE] {s.id}: {s.title}")
if not_done:
print(f"\n Not Completed (carry over):")
for s in not_done:
print(f" [{s.status.value}] {s.id}: {s.title}")
def retrospective(self):
"""Sprint Retrospective"""
print(f"\n{'='*55}")
print(f"Sprint Retrospective")
print(f"{'='*55}")
print(f" What went well?")
print(f" - ทีมส่งมอบตาม Sprint Goal")
print(f" - Code Review ดีขึ้น")
print(f" What could improve?")
print(f" - Estimation ยังไม่แม่นยำ")
print(f" - Technical Debt สะสม")
print(f" Action Items:")
print(f" [ ] ปรับ Estimation ใช้ Planning Poker")
print(f" [ ] จัดสรรเวลา 20% สำหรับ Tech Debt")
# ตัวอย่าง
board = ScrumBoard("Team Alpha", velocity=30)
# สร้าง User Stories
board.create_story("User Login", "ผู้ใช้สามารถ Login ด้วย Email/Password",
["Login สำเร็จแสดง Dashboard", "Login ผิดแสดง Error"], 5, Priority.HIGH)
board.create_story("User Registration", "ผู้ใช้สามารถสมัครสมาชิก",
["กรอก Email, Password, Name", "ส่ง Verification Email"], 8, Priority.HIGH)
board.create_story("Dashboard", "แสดง Dashboard หลัง Login",
["แสดง Summary", "แสดง Recent Activity"], 5, Priority.MEDIUM)
board.create_story("Profile Settings", "ผู้ใช้แก้ไข Profile ได้",
["เปลี่ยนชื่อ", "เปลี่ยนรูป"], 3, Priority.LOW)
# Sprint Planning
board.sprint_planning("Sprint 1", "User Authentication", datetime(2024, 1, 15))
board.daily_scrum()
Agile Metrics และ Reporting
# agile_metrics.py — Agile Metrics Dashboard
class AgileMetrics:
"""คำนวณ Agile Metrics"""
def __init__(self, sprints):
self.sprints = sprints
def velocity_chart(self):
"""Velocity Chart"""
print(f"\nVelocity Chart")
print(f"{'─'*40}")
velocities = []
for sprint in self.sprints:
vel = sprint.completed_points
velocities.append(vel)
bar = "█" * (vel // 2)
print(f" {sprint.name:<12} {bar} {vel} pts")
if velocities:
avg = sum(velocities) / len(velocities)
print(f"\n Average Velocity: {avg:.0f} points/sprint")
print(f" Trend: {'↑' if velocities[-1] > avg else '↓'}")
def burndown(self, sprint):
"""Sprint Burndown"""
total = sprint.total_points
remaining = total - sprint.completed_points
days = sprint.duration_weeks * 5 # working days
print(f"\nBurndown: {sprint.name}")
print(f" Total: {total} pts | Remaining: {remaining} pts")
print(f" Ideal: {total/days:.1f} pts/day")
# Simulate burndown
ideal_per_day = total / days
for day in range(days + 1):
ideal = max(0, total - ideal_per_day * day)
actual = max(0, remaining * (days - day) / days)
bar_ideal = "·" * int(ideal / 2)
bar_actual = "█" * int(actual / 2)
if day % 2 == 0:
print(f" Day {day:>2}: {bar_actual}")
def cumulative_flow(self):
"""Cumulative Flow Diagram"""
print(f"\nCumulative Flow")
for sprint in self.sprints:
done = sum(1 for s in sprint.stories if s.status == Status.DONE)
ip = sum(1 for s in sprint.stories if s.status == Status.IN_PROGRESS)
todo = sum(1 for s in sprint.stories if s.status == Status.TODO)
print(f" {sprint.name}: Done={done} InProgress={ip} ToDo={todo}")
def team_health(self):
"""Team Health Metrics"""
if not self.sprints:
return
last = self.sprints[-1]
commitment = last.total_points
delivered = last.completed_points
ratio = delivered / commitment * 100 if commitment > 0 else 0
print(f"\nTeam Health")
print(f" Commitment: {commitment} pts")
print(f" Delivered: {delivered} pts")
print(f" Delivery Ratio: {ratio:.0f}%")
print(f" Sprint Goal: {'Achieved' if ratio >= 80 else 'Not Achieved'}")
health_indicators = [
("Sprint Goal Achievement", ratio >= 80),
("Velocity Stable", True),
("Team Morale", True),
("Technical Debt Manageable", True),
("Stakeholder Satisfied", ratio >= 70),
]
print(f"\n Health Check:")
for indicator, healthy in health_indicators:
status = "✓" if healthy else "✗"
print(f" [{status}] {indicator}")
# ตัวอย่าง Sprint Data
sprint1 = Sprint("Sprint 1", "Auth", datetime(2024, 1, 15))
sprint1.stories = [
UserStory("US-1", "Login", "", [], 5, Priority.HIGH, Status.DONE),
UserStory("US-2", "Register", "", [], 8, Priority.HIGH, Status.DONE),
UserStory("US-3", "Dashboard", "", [], 5, Priority.MEDIUM, Status.DONE),
]
sprint2 = Sprint("Sprint 2", "Profile", datetime(2024, 1, 29))
sprint2.stories = [
UserStory("US-4", "Profile", "", [], 3, Priority.LOW, Status.DONE),
UserStory("US-5", "Settings", "", [], 5, Priority.MEDIUM, Status.DONE),
UserStory("US-6", "Notifications", "", [], 8, Priority.HIGH, Status.IN_PROGRESS),
]
metrics = AgileMetrics([sprint1, sprint2])
metrics.velocity_chart()
metrics.team_health()
Scrum Tools Comparison
# scrum_tools.py — เปรียบเทียบ Scrum Tools
tools = [
{
"name": "Jira",
"company": "Atlassian",
"price": "ฟรี 10 users, $7.75/user/mo",
"pros": ["Features ครบที่สุด", "Integrations มากมาย", "Custom Workflows"],
"cons": ["ซับซ้อน", "ช้าบางครั้ง", "แพง"],
"best_for": "Enterprise, ทีมใหญ่",
},
{
"name": "Linear",
"company": "Linear",
"price": "ฟรี 250 issues, $8/user/mo",
"pros": ["UI สวยเร็ว", "Keyboard Shortcuts", "GitHub Integration ดีเยี่ยม"],
"cons": ["Features น้อยกว่า Jira", "ไม่มี Time Tracking"],
"best_for": "Startup, ทีม Dev",
},
{
"name": "Notion",
"company": "Notion",
"price": "ฟรี, $8/user/mo",
"pros": ["ยืดหยุ่นมาก", "Docs + Tasks ที่เดียว", "Template เยอะ"],
"cons": ["ไม่ได้ออกแบบเฉพาะ Scrum", "ช้าเมื่อข้อมูลเยอะ"],
"best_for": "ทีมเล็ก, Non-tech Teams",
},
{
"name": "GitHub Projects",
"company": "GitHub",
"price": "ฟรี",
"pros": ["อยู่กับ Code เลย", "ฟรี", "Automation ง่าย"],
"cons": ["Features จำกัด", "ไม่มี Burndown Chart"],
"best_for": "Open Source, ทีม Dev เล็ก",
},
]
print("Scrum Tools Comparison")
print("=" * 55)
for tool in tools:
print(f"\n {tool['name']} ({tool['company']})")
print(f" Price: {tool['price']}")
print(f" Best for: {tool['best_for']}")
print(f" Pros: {', '.join(tool['pros'])}")
print(f" Cons: {', '.join(tool['cons'])}")
เคล็ดลับ
- Sprint Goal: ทุก Sprint ต้องมี Sprint Goal ที่ชัดเจน เป็นเป้าหมายเดียวที่ทีมมุ่งไป
- Timeboxing: ทุก Event มี Time Box ห้ามเกิน Daily Scrum 15 นาที Sprint Planning 8 ชั่วโมง
- Definition of Done: กำหนด DoD ให้ชัดเจน เช่น Code Review ผ่าน Tests ผ่าน Documentation อัพเดท
- Retrospective: ทำ Retrospective ทุก Sprint และลงมือแก้ไข Action Items จริง
- Cross-functional Team: ทีมต้องมีทุกทักษะที่จำเป็น ไม่ต้องพึ่งทีมอื่น
- Sustainable Pace: ไม่ Overcommit อย่าใส่งานเกิน Velocity ของทีม
Agile คืออะไร
แนวทางพัฒนาซอฟต์แวร์เน้นส่งมอบงานรอบสั้นๆ ปรับเปลี่ยนตาม Feedback Individuals and Interactions Working Software Customer Collaboration Responding to Change ตาม Agile Manifesto
Scrum คืออะไร
Framework ภายใต้ Agile แบ่งงานเป็น Sprint 1-4 สัปดาห์ มี 3 Roles (PO SM Dev) 5 Events (Planning Daily Review Retro Sprint) 3 Artifacts (Product Backlog Sprint Backlog Increment)
Scrum Master ทำหน้าที่อะไร
Servant Leader ดูแลทีมตาม Scrum Framework ขจัดอุปสรรค Facilitate Events Coach Agile Practices ปกป้องทีมจากรบกวนภายนอก ไม่ใช่ Project Manager
Sprint Planning ทำอย่างไร
Meeting ตอนเริ่ม Sprint เลือก Backlog Items กำหนด Sprint Goal Break Down เป็น Tasks Estimate ด้วย Story Points ใช้เวลาไม่เกิน 8 ชั่วโมงสำหรับ Sprint 4 สัปดาห์
สรุป
Agile Scrum เป็น Framework ที่ช่วยทีมพัฒนาซอฟต์แวร์ส่งมอบงานเร็วขึ้นและมีคุณภาพ แบ่งงานเป็น Sprint มี Sprint Goal ชัดเจน ทำ Daily Scrum ทุกวัน Sprint Review Demo ให้ Stakeholders Sprint Retrospective ปรับปรุงกระบวนการ ใช้ Velocity วัด Performance Burndown Chart ติดตาม Progress
