Software Development Lifecycle (SDLC)

Learning Objectives

By the end of this reading, you will be able to:

  • Understand the phases of the Software Development Lifecycle
  • Compare different SDLC methodologies (Waterfall, Agile, Scrum, Kanban)
  • Apply Agile principles in software development
  • Conduct requirements gathering and analysis
  • Plan and estimate software projects effectively
  • Choose appropriate methodologies for different project types

Introduction

The Software Development Lifecycle (SDLC) is a systematic process for planning, creating, testing, and deploying software systems. Understanding SDLC methodologies is crucial for delivering high-quality software on time and within budget.

Why SDLC Matters

  1. Structure: Provides a framework for development activities
  2. Quality: Ensures systematic testing and validation
  3. Predictability: Makes timelines and costs more predictable
  4. Communication: Establishes clear expectations among stakeholders
  5. Risk Management: Identifies and mitigates risks early

SDLC Phases

All SDLC methodologies include these core phases, though their implementation varies:

1. Planning

Defining project scope, objectives, and feasibility.

class ProjectPlan:
    """Represents a software project plan."""

    def __init__(self, name, stakeholders):
        self.name = name
        self.stakeholders = stakeholders
        self.objectives = []
        self.constraints = {}
        self.risks = []

    def add_objective(self, objective, success_criteria):
        """Add a project objective with measurable criteria."""
        self.objectives.append({
            'description': objective,
            'success_criteria': success_criteria,
            'status': 'pending'
        })

    def add_constraint(self, type_, description):
        """
        Add project constraints.

        Args:
            type_: 'budget', 'time', 'resources', 'technical'
            description: Details of the constraint
        """
        if type_ not in self.constraints:
            self.constraints[type_] = []
        self.constraints[type_].append(description)

    def assess_risk(self, risk, probability, impact, mitigation):
        """
        Document project risks.

        Args:
            risk (str): Description of the risk
            probability (str): 'low', 'medium', 'high'
            impact (str): 'low', 'medium', 'high'
            mitigation (str): Mitigation strategy
        """
        self.risks.append({
            'risk': risk,
            'probability': probability,
            'impact': impact,
            'mitigation': mitigation,
            'priority': self._calculate_priority(probability, impact)
        })

    def _calculate_priority(self, probability, impact):
        """Calculate risk priority based on probability and impact."""
        levels = {'low': 1, 'medium': 2, 'high': 3}
        score = levels[probability] * levels[impact]
        if score >= 6:
            return 'critical'
        elif score >= 4:
            return 'high'
        else:
            return 'medium'

# Example usage
project = ProjectPlan("E-commerce Platform", ["CEO", "CTO", "Product Manager"])
project.add_objective(
    "Support 10,000 concurrent users",
    "Load testing shows <2s response time at 10k users"
)
project.add_constraint("budget", "$500,000 total development cost")
project.add_constraint("time", "6 months to MVP launch")
project.assess_risk(
    "Third-party payment API downtime",
    probability="medium",
    impact="high",
    mitigation="Implement fallback payment provider"
)

2. Requirements Gathering

Collecting and documenting what the software should do.

from enum import Enum
from typing import List, Optional

class RequirementType(Enum):
    FUNCTIONAL = "functional"
    NON_FUNCTIONAL = "non_functional"
    TECHNICAL = "technical"
    BUSINESS = "business"

class Priority(Enum):
    MUST_HAVE = "must_have"
    SHOULD_HAVE = "should_have"
    COULD_HAVE = "could_have"
    WONT_HAVE = "wont_have"  # MoSCoW prioritization

class Requirement:
    """Represents a single requirement."""

    def __init__(self, id_, description, type_, priority):
        self.id = id_
        self.description = description
        self.type = type_
        self.priority = priority
        self.acceptance_criteria = []
        self.dependencies = []
        self.status = "draft"

    def add_acceptance_criterion(self, criterion):
        """Add criterion that must be met for requirement to be satisfied."""
        self.acceptance_criteria.append(criterion)

    def add_dependency(self, requirement_id):
        """Add dependency on another requirement."""
        self.dependencies.append(requirement_id)

class RequirementsDocument:
    """Manages all project requirements."""

    def __init__(self, project_name):
        self.project_name = project_name
        self.requirements = {}

    def add_requirement(self, requirement):
        """Add a requirement to the document."""
        self.requirements[requirement.id] = requirement

    def get_by_priority(self, priority):
        """Get all requirements with specified priority."""
        return [req for req in self.requirements.values()
                if req.priority == priority]

    def get_dependencies(self, requirement_id):
        """Get all requirements that depend on the given requirement."""
        dependents = []
        for req in self.requirements.values():
            if requirement_id in req.dependencies:
                dependents.append(req)
        return dependents

# Example: E-commerce requirements
reqs = RequirementsDocument("E-commerce Platform")

# Functional requirement
user_login = Requirement(
    "FR-001",
    "Users must be able to log in with email and password",
    RequirementType.FUNCTIONAL,
    Priority.MUST_HAVE
)
user_login.add_acceptance_criterion(
    "Email validation follows RFC 5322 standard"
)
user_login.add_acceptance_criterion(
    "Passwords must be at least 8 characters"
)
user_login.add_acceptance_criterion(
    "Failed login attempts are rate-limited"
)
reqs.add_requirement(user_login)

# Non-functional requirement
performance = Requirement(
    "NFR-001",
    "System must handle 10,000 concurrent users",
    RequirementType.NON_FUNCTIONAL,
    Priority.MUST_HAVE
)
performance.add_acceptance_criterion(
    "Page load time < 2 seconds at 95th percentile"
)
performance.add_acceptance_criterion(
    "API response time < 500ms at 99th percentile"
)
reqs.add_requirement(performance)

# Business requirement
revenue = Requirement(
    "BR-001",
    "Platform must support multiple payment methods",
    RequirementType.BUSINESS,
    Priority.MUST_HAVE
)
revenue.add_acceptance_criterion("Support credit/debit cards")
revenue.add_acceptance_criterion("Support PayPal")
revenue.add_acceptance_criterion("Support Apple Pay and Google Pay")
reqs.add_requirement(revenue)

3. Design

Creating the architecture and detailed design of the system.

class SystemDesign:
    """Represents high-level system architecture."""

    def __init__(self, system_name):
        self.system_name = system_name
        self.components = {}
        self.interfaces = []
        self.data_models = {}

    def add_component(self, name, responsibility, technology):
        """
        Add a system component.

        Args:
            name: Component name (e.g., "API Server")
            responsibility: What the component does
            technology: Tech stack (e.g., "Python/FastAPI")
        """
        self.components[name] = {
            'responsibility': responsibility,
            'technology': technology,
            'dependencies': []
        }

    def add_interface(self, from_component, to_component, protocol):
        """Define how components communicate."""
        self.interfaces.append({
            'from': from_component,
            'to': to_component,
            'protocol': protocol
        })

# Example: Microservices architecture
design = SystemDesign("E-commerce Platform")

design.add_component(
    "API Gateway",
    "Route requests, authentication, rate limiting",
    "Kong/Nginx"
)
design.add_component(
    "User Service",
    "User authentication, profiles, preferences",
    "Python/FastAPI + PostgreSQL"
)
design.add_component(
    "Product Service",
    "Product catalog, search, inventory",
    "Python/FastAPI + Elasticsearch"
)
design.add_component(
    "Order Service",
    "Shopping cart, checkout, order management",
    "Python/FastAPI + PostgreSQL"
)
design.add_component(
    "Payment Service",
    "Payment processing, transaction management",
    "Python/FastAPI + Stripe API"
)

design.add_interface("Client", "API Gateway", "HTTPS/REST")
design.add_interface("API Gateway", "User Service", "gRPC")
design.add_interface("API Gateway", "Product Service", "gRPC")
design.add_interface("Order Service", "Payment Service", "gRPC")

4. Implementation

Writing the actual code.

# This is where development happens - covered in other modules
# Focus here is on organizing the implementation phase

class Sprint:
    """Represents a development sprint (Agile concept)."""

    def __init__(self, number, duration_days=14):
        self.number = number
        self.duration_days = duration_days
        self.stories = []
        self.capacity_hours = 0

    def add_story(self, story_id, description, points):
        """Add a user story to the sprint."""
        self.stories.append({
            'id': story_id,
            'description': description,
            'points': points,
            'status': 'todo',
            'tasks': []
        })

    def set_team_capacity(self, team_size, hours_per_person):
        """Set available development hours for the sprint."""
        self.capacity_hours = team_size * hours_per_person

    def get_velocity(self):
        """Calculate completed story points."""
        return sum(s['points'] for s in self.stories
                  if s['status'] == 'done')

# Example sprint planning
sprint_1 = Sprint(number=1, duration_days=14)
sprint_1.set_team_capacity(team_size=5, hours_per_person=60)
sprint_1.add_story(
    "US-101",
    "As a user, I can create an account",
    points=5
)
sprint_1.add_story(
    "US-102",
    "As a user, I can log in",
    points=3
)
sprint_1.add_story(
    "US-103",
    "As a user, I can reset my password",
    points=5
)

5. Testing

Verifying the software works correctly.

class TestPlan:
    """Manages testing strategy and execution."""

    def __init__(self, project_name):
        self.project_name = project_name
        self.test_cases = []
        self.coverage_target = 80  # Percentage

    def add_test_case(self, id_, description, type_, priority):
        """
        Add a test case.

        Args:
            type_: 'unit', 'integration', 'system', 'acceptance'
        """
        self.test_cases.append({
            'id': id_,
            'description': description,
            'type': type_,
            'priority': priority,
            'status': 'pending',
            'steps': [],
            'expected_result': None
        })

    def get_test_summary(self):
        """Generate testing progress summary."""
        total = len(self.test_cases)
        passed = sum(1 for tc in self.test_cases if tc['status'] == 'passed')
        failed = sum(1 for tc in self.test_cases if tc['status'] == 'failed')

        return {
            'total': total,
            'passed': passed,
            'failed': failed,
            'pending': total - passed - failed,
            'pass_rate': (passed / total * 100) if total > 0 else 0
        }

# Example test planning
test_plan = TestPlan("E-commerce Platform")
test_plan.add_test_case(
    "TC-001",
    "Verify user can register with valid email",
    type_='integration',
    priority='high'
)
test_plan.add_test_case(
    "TC-002",
    "Verify user cannot register with invalid email",
    type_='integration',
    priority='high'
)

6. Deployment

Releasing the software to production.

class DeploymentPipeline:
    """Represents a CI/CD deployment pipeline."""

    def __init__(self, app_name):
        self.app_name = app_name
        self.stages = []

    def add_stage(self, name, steps):
        """Add a deployment stage."""
        self.stages.append({
            'name': name,
            'steps': steps,
            'status': 'pending'
        })

# Example deployment pipeline
pipeline = DeploymentPipeline("ecommerce-api")

pipeline.add_stage("Build", [
    "Install dependencies",
    "Run linting",
    "Compile code",
    "Create Docker image"
])

pipeline.add_stage("Test", [
    "Run unit tests",
    "Run integration tests",
    "Check code coverage (>80%)",
    "Run security scan"
])

pipeline.add_stage("Deploy to Staging", [
    "Deploy to staging environment",
    "Run smoke tests",
    "Run acceptance tests"
])

pipeline.add_stage("Deploy to Production", [
    "Create backup",
    "Deploy with blue-green strategy",
    "Run health checks",
    "Monitor error rates"
])

7. Maintenance

Ongoing support and updates.

class MaintenanceTracker:
    """Track maintenance activities."""

    def __init__(self):
        self.incidents = []
        self.enhancements = []

    def log_incident(self, severity, description):
        """Log a production incident."""
        self.incidents.append({
            'severity': severity,  # 'critical', 'high', 'medium', 'low'
            'description': description,
            'timestamp': None,  # Would use datetime.now()
            'status': 'open',
            'resolution': None
        })

    def plan_enhancement(self, description, effort):
        """Plan a system enhancement."""
        self.enhancements.append({
            'description': description,
            'effort': effort,  # Story points or hours
            'status': 'planned'
        })

Waterfall Methodology

Sequential approach where each phase must complete before the next begins.

Waterfall Phases

"""
Waterfall Flow:

Requirements → Design → Implementation → Testing → Deployment → Maintenance
     ↓            ↓            ↓              ↓           ↓            ↓
  Complete    Complete     Complete       Complete   Complete     Ongoing

Characteristics:
- Linear and sequential
- Extensive documentation
- Clear milestones
- Changes are costly
- Works well for stable requirements
"""

class WaterfallProject:
    """Manages a waterfall-style project."""

    PHASES = [
        'requirements',
        'design',
        'implementation',
        'testing',
        'deployment',
        'maintenance'
    ]

    def __init__(self, name):
        self.name = name
        self.current_phase = 0
        self.phase_artifacts = {phase: [] for phase in self.PHASES}

    def complete_phase(self, deliverables):
        """
        Complete current phase and move to next.

        Args:
            deliverables: List of artifacts/documents produced
        """
        current = self.PHASES[self.current_phase]
        self.phase_artifacts[current] = deliverables

        # Phase gate: verify deliverables
        if not self._verify_deliverables(current, deliverables):
            raise ValueError(f"Phase {current} deliverables incomplete")

        self.current_phase += 1
        return self.PHASES[self.current_phase] if self.current_phase < len(self.PHASES) else 'Complete'

    def _verify_deliverables(self, phase, deliverables):
        """Verify phase deliverables are complete."""
        required = {
            'requirements': ['Requirements Document', 'Use Cases'],
            'design': ['Architecture Document', 'Database Schema', 'UI Mockups'],
            'implementation': ['Source Code', 'Code Documentation'],
            'testing': ['Test Cases', 'Test Results', 'Bug Reports'],
            'deployment': ['Deployment Guide', 'User Manual'],
            'maintenance': ['Maintenance Plan', 'SLA Document']
        }
        return all(req in deliverables for req in required.get(phase, []))

# Example waterfall project
waterfall = WaterfallProject("Banking System")
next_phase = waterfall.complete_phase([
    'Requirements Document',
    'Use Cases'
])
print(f"Moving to: {next_phase}")  # Moving to: design

Waterfall Pros and Cons

"""
PROS:
✓ Simple to understand and manage
✓ Clear milestones and deliverables
✓ Extensive documentation
✓ Works well for projects with stable requirements
✓ Easy to estimate costs and timelines

CONS:
✗ Inflexible to changing requirements
✗ Late testing discovers issues late
✗ No working software until late in project
✗ High risk for complex projects
✗ Difficult to incorporate customer feedback

BEST FOR:
- Small, well-defined projects
- Projects with stable requirements
- Regulatory/compliance-heavy projects
- Projects with fixed budgets and timelines
"""

Agile Methodology

Iterative approach emphasizing flexibility, collaboration, and customer feedback.

Agile Manifesto Principles

"""
Agile Values:

1. Individuals and interactions > Processes and tools
2. Working software > Comprehensive documentation
3. Customer collaboration > Contract negotiation
4. Responding to change > Following a plan

12 Principles:
1. Customer satisfaction through early and continuous delivery
2. Welcome changing requirements
3. Deliver working software frequently (weeks not months)
4. Business people and developers work together daily
5. Build projects around motivated individuals
6. Face-to-face conversation is best
7. Working software is primary measure of progress
8. Sustainable development pace
9. Continuous attention to technical excellence
10. Simplicity - maximize work not done
11. Self-organizing teams
12. Regular reflection and adjustment
"""

Agile Implementation

class AgileProject:
    """Manages an Agile project with iterations."""

    def __init__(self, name, iteration_length_days=14):
        self.name = name
        self.iteration_length = iteration_length_days
        self.iterations = []
        self.backlog = []
        self.current_iteration = None

    def add_to_backlog(self, story):
        """Add user story to product backlog."""
        self.backlog.append(story)
        # Sort by priority
        self.backlog.sort(key=lambda s: s.get('priority', 999))

    def start_iteration(self, iteration_number):
        """Start a new iteration."""
        self.current_iteration = {
            'number': iteration_number,
            'stories': [],
            'committed_points': 0,
            'completed_points': 0,
            'status': 'active'
        }

    def commit_story(self, story_id):
        """Commit a story from backlog to current iteration."""
        story = next((s for s in self.backlog if s['id'] == story_id), None)
        if story and self.current_iteration:
            self.backlog.remove(story)
            self.current_iteration['stories'].append(story)
            self.current_iteration['committed_points'] += story['points']

    def complete_story(self, story_id):
        """Mark story as complete."""
        if self.current_iteration:
            story = next((s for s in self.current_iteration['stories']
                         if s['id'] == story_id), None)
            if story:
                story['status'] = 'done'
                self.current_iteration['completed_points'] += story['points']

    def end_iteration(self):
        """End current iteration and calculate velocity."""
        if self.current_iteration:
            self.current_iteration['status'] = 'complete'
            self.current_iteration['velocity'] = \
                self.current_iteration['completed_points']

            # Incomplete stories go back to backlog
            incomplete = [s for s in self.current_iteration['stories']
                         if s.get('status') != 'done']
            self.backlog.extend(incomplete)

            self.iterations.append(self.current_iteration)
            velocity = self.current_iteration['velocity']
            self.current_iteration = None
            return velocity

    def get_average_velocity(self):
        """Calculate team's average velocity over recent iterations."""
        if not self.iterations:
            return 0
        recent = self.iterations[-3:]  # Last 3 iterations
        return sum(i['velocity'] for i in recent) / len(recent)

# Example Agile project
agile = AgileProject("Mobile App", iteration_length_days=14)

# Build backlog
agile.add_to_backlog({
    'id': 'US-001',
    'description': 'As a user, I can create an account',
    'points': 5,
    'priority': 1
})
agile.add_to_backlog({
    'id': 'US-002',
    'description': 'As a user, I can log in',
    'points': 3,
    'priority': 1
})
agile.add_to_backlog({
    'id': 'US-003',
    'description': 'As a user, I can view my profile',
    'points': 2,
    'priority': 2
})

# Iteration 1
agile.start_iteration(1)
agile.commit_story('US-001')
agile.commit_story('US-002')
agile.complete_story('US-001')
agile.complete_story('US-002')
velocity = agile.end_iteration()
print(f"Iteration 1 velocity: {velocity}")  # 8 points

Scrum Framework

Specific Agile framework with defined roles, events, and artifacts.

Scrum Roles

class ScrumTeam:
    """Represents a Scrum team with defined roles."""

    def __init__(self, name):
        self.name = name
        self.product_owner = None
        self.scrum_master = None
        self.developers = []

    def set_product_owner(self, person):
        """
        Product Owner responsibilities:
        - Maximize product value
        - Manage product backlog
        - Ensure backlog is visible and understood
        - Prioritize work
        """
        self.product_owner = person

    def set_scrum_master(self, person):
        """
        Scrum Master responsibilities:
        - Coach team on Scrum
        - Facilitate events
        - Remove impediments
        - Shield team from distractions
        """
        self.scrum_master = person

    def add_developer(self, person):
        """
        Developer responsibilities:
        - Create plan for Sprint (Sprint Backlog)
        - Adapt plan each day toward Sprint Goal
        - Hold each other accountable as professionals
        """
        self.developers.append(person)

class ScrumEvents:
    """Scrum ceremonies/events."""

    @staticmethod
    def sprint_planning(backlog, capacity):
        """
        Sprint Planning event.

        Questions answered:
        1. What can be delivered in this Sprint?
        2. How will the work get done?

        Duration: Max 8 hours for 1-month Sprint
        """
        sprint_goal = "Define what we want to achieve"
        sprint_backlog = []
        points = 0

        # Select items from backlog that fit in capacity
        for item in backlog:
            if points + item['points'] <= capacity:
                sprint_backlog.append(item)
                points += item['points']

        return {
            'goal': sprint_goal,
            'backlog': sprint_backlog,
            'committed_points': points
        }

    @staticmethod
    def daily_standup():
        """
        Daily Scrum event.

        Each team member answers:
        1. What did I do yesterday?
        2. What will I do today?
        3. Are there any impediments?

        Duration: 15 minutes max
        """
        return {
            'duration_minutes': 15,
            'questions': [
                'What did you complete yesterday?',
                'What will you work on today?',
                'Any blockers or impediments?'
            ]
        }

    @staticmethod
    def sprint_review(increment, stakeholders):
        """
        Sprint Review event.

        Purpose: Inspect increment and adapt backlog
        Attendees: Scrum Team + stakeholders
        Duration: Max 4 hours for 1-month Sprint
        """
        feedback = []
        # Demonstrate working software
        # Gather feedback
        # Update backlog based on feedback
        return feedback

    @staticmethod
    def sprint_retrospective(team):
        """
        Sprint Retrospective event.

        Purpose: Inspect how last Sprint went
        Questions:
        1. What went well?
        2. What could be improved?
        3. What will we commit to improve?

        Duration: Max 3 hours for 1-month Sprint
        """
        return {
            'went_well': [],
            'to_improve': [],
            'action_items': []
        }

# Example Scrum implementation
team = ScrumTeam("Platform Team")
team.set_product_owner("Alice")
team.set_scrum_master("Bob")
team.add_developer("Charlie")
team.add_developer("Diana")
team.add_developer("Eve")

# Sprint planning
backlog = [
    {'id': 'US-001', 'points': 5, 'description': 'User login'},
    {'id': 'US-002', 'points': 3, 'description': 'Password reset'},
    {'id': 'US-003', 'points': 8, 'description': 'User dashboard'},
]
sprint = ScrumEvents.sprint_planning(backlog, capacity=13)
print(f"Sprint committed points: {sprint['committed_points']}")  # 8

Scrum Artifacts

class ProductBacklog:
    """Ordered list of what is needed to improve the product."""

    def __init__(self):
        self.items = []

    def add_item(self, id_, description, value, effort):
        """Add item to backlog."""
        self.items.append({
            'id': id_,
            'description': description,
            'business_value': value,
            'effort': effort,
            'priority': value / effort,  # Simple prioritization
            'status': 'backlog'
        })
        # Re-sort by priority
        self.items.sort(key=lambda x: x['priority'], reverse=True)

class SprintBacklog:
    """Work selected for the Sprint plus plan for delivering it."""

    def __init__(self, sprint_goal):
        self.sprint_goal = sprint_goal
        self.items = []
        self.tasks = []

    def add_item(self, item):
        """Add item from product backlog to sprint."""
        self.items.append(item)

    def break_into_tasks(self, item_id, tasks):
        """Break user story into specific tasks."""
        for task in tasks:
            self.tasks.append({
                'item_id': item_id,
                'description': task,
                'status': 'todo',
                'assigned_to': None
            })

class Increment:
    """Sum of all Product Backlog items completed in Sprint + previous Sprints."""

    def __init__(self):
        self.completed_items = []
        self.version = "0.0.0"

    def add_completed_item(self, item):
        """Add completed item to increment."""
        if self._meets_definition_of_done(item):
            self.completed_items.append(item)

    def _meets_definition_of_done(self, item):
        """
        Check if item meets Definition of Done.

        Example DoD:
        - Code written and reviewed
        - Unit tests written and passing
        - Integration tests passing
        - Documentation updated
        - Accepted by Product Owner
        """
        return True  # Simplified

Kanban Methodology

Flow-based approach focusing on continuous delivery and limiting work in progress.

Kanban Board

class KanbanBoard:
    """Visual workflow management system."""

    def __init__(self, name):
        self.name = name
        self.columns = {}
        self.wip_limits = {}
        self.items = []

    def add_column(self, name, wip_limit=None):
        """
        Add a column to the board.

        Common columns: Backlog → To Do → In Progress → Review → Done
        """
        self.columns[name] = []
        if wip_limit:
            self.wip_limits[name] = wip_limit

    def add_item(self, item):
        """Add work item to the board (starts in first column)."""
        first_column = list(self.columns.keys())[0]
        item['column'] = first_column
        self.items.append(item)
        self.columns[first_column].append(item['id'])

    def move_item(self, item_id, to_column):
        """Move item to different column."""
        item = next((i for i in self.items if i['id'] == item_id), None)
        if not item:
            return False

        # Check WIP limit
        if to_column in self.wip_limits:
            current_wip = len(self.columns[to_column])
            if current_wip >= self.wip_limits[to_column]:
                raise ValueError(f"WIP limit reached for {to_column}")

        # Move item
        from_column = item['column']
        self.columns[from_column].remove(item_id)
        self.columns[to_column].append(item_id)
        item['column'] = to_column

    def get_metrics(self):
        """Calculate Kanban metrics."""
        return {
            'lead_time': self._calculate_lead_time(),
            'cycle_time': self._calculate_cycle_time(),
            'throughput': self._calculate_throughput(),
            'wip': sum(len(items) for col, items in self.columns.items()
                      if col not in ['Backlog', 'Done'])
        }

    def _calculate_lead_time(self):
        """Time from request to delivery."""
        # Simplified - would calculate from timestamps
        return 0

    def _calculate_cycle_time(self):
        """Time from start to finish."""
        # Simplified - would calculate from timestamps
        return 0

    def _calculate_throughput(self):
        """Items completed per time period."""
        done_column = 'Done'
        return len(self.columns.get(done_column, []))

# Example Kanban board
board = KanbanBoard("Development Board")
board.add_column("Backlog")
board.add_column("To Do")
board.add_column("In Progress", wip_limit=3)
board.add_column("Code Review", wip_limit=2)
board.add_column("Testing", wip_limit=2)
board.add_column("Done")

board.add_item({
    'id': 'TASK-001',
    'title': 'Implement user authentication',
    'type': 'feature',
    'priority': 'high'
})

board.move_item('TASK-001', 'To Do')
board.move_item('TASK-001', 'In Progress')

Kanban Principles

"""
KANBAN PRINCIPLES:

1. Visualize Workflow
   - Make work visible
   - See bottlenecks
   - Track progress

2. Limit Work in Progress (WIP)
   - Focus on finishing over starting
   - Reduce context switching
   - Improve flow

3. Manage Flow
   - Measure lead time and cycle time
   - Optimize process
   - Predict delivery

4. Make Process Policies Explicit
   - Clear definitions
   - Shared understanding
   - Consistent application

5. Implement Feedback Loops
   - Regular reviews
   - Continuous improvement
   - Adapt to change

6. Improve Collaboratively
   - Evolutionary change
   - Team-driven improvements
   - Experiment and adapt

KANBAN vs SCRUM:

Kanban:
- Continuous flow
- No fixed iterations
- No prescribed roles
- Change anytime
- WIP limits
- Lead time focused

Scrum:
- Fixed-length sprints
- Prescribed ceremonies
- Defined roles (PO, SM, Dev)
- Change between sprints
- Velocity focused
- Commitment driven
"""

Choosing a Methodology

class MethodologySelector:
    """Help choose appropriate SDLC methodology."""

    @staticmethod
    def recommend(project_characteristics):
        """
        Recommend methodology based on project characteristics.

        Args:
            project_characteristics: Dict with keys:
                - requirements_stability: 'stable' or 'changing'
                - team_size: int
                - timeline: 'short' or 'long'
                - complexity: 'low', 'medium', 'high'
                - customer_involvement: 'low' or 'high'
        """
        score = {
            'waterfall': 0,
            'agile_scrum': 0,
            'kanban': 0
        }

        # Stable requirements favor waterfall
        if project_characteristics['requirements_stability'] == 'stable':
            score['waterfall'] += 2
        else:
            score['agile_scrum'] += 2
            score['kanban'] += 2

        # Small teams work well with kanban
        team_size = project_characteristics['team_size']
        if team_size <= 5:
            score['kanban'] += 1
        elif team_size <= 9:
            score['agile_scrum'] += 2
        else:
            score['waterfall'] += 1

        # High complexity benefits from agile
        if project_characteristics['complexity'] == 'high':
            score['agile_scrum'] += 2
            score['kanban'] += 1

        # Customer involvement
        if project_characteristics['customer_involvement'] == 'high':
            score['agile_scrum'] += 2
            score['kanban'] += 1
        else:
            score['waterfall'] += 1

        # Return recommendation
        best = max(score, key=score.get)
        return {
            'recommended': best,
            'scores': score,
            'rationale': f"Based on project characteristics, {best} scores highest"
        }

# Example usage
project = {
    'requirements_stability': 'changing',
    'team_size': 6,
    'timeline': 'long',
    'complexity': 'high',
    'customer_involvement': 'high'
}
recommendation = MethodologySelector.recommend(project)
print(recommendation['recommended'])  # Likely agile_scrum

Estimation Techniques

Story Points

class StoryPointEstimation:
    """Estimate work using story points (Fibonacci sequence)."""

    FIBONACCI = [1, 2, 3, 5, 8, 13, 21]

    @staticmethod
    def planning_poker(story, team_estimates):
        """
        Planning poker estimation technique.

        Args:
            story: User story to estimate
            team_estimates: List of estimates from team members

        Returns:
            Consensus estimate
        """
        # If all estimates are close, use average
        if max(team_estimates) - min(team_estimates) <= 3:
            avg = sum(team_estimates) / len(team_estimates)
            # Round to nearest Fibonacci number
            return min(StoryPointEstimation.FIBONACCI,
                      key=lambda x: abs(x - avg))

        # Otherwise, discuss and re-estimate
        return None  # Need another round

    @staticmethod
    def t_shirt_sizing(story):
        """
        Simple relative sizing: XS, S, M, L, XL.

        Useful for initial rough estimates.
        """
        sizes = {
            'XS': 1,
            'S': 2,
            'M': 5,
            'L': 8,
            'XL': 13
        }
        # Return size that can be converted to points later
        return sizes

# Example estimation
team_estimates = [5, 5, 8, 5]  # Four team members
consensus = StoryPointEstimation.planning_poker("User login feature", team_estimates)
print(f"Consensus estimate: {consensus} points")  # Likely 5

Exercises

Basic Exercises

  1. SDLC Phases

    • List all 7 SDLC phases
    • For each phase, describe the main activities and deliverables
    • Create a simple project plan for a calculator app
  2. Requirements Gathering

    • Create a requirements document for a library management system
    • Include at least 5 functional requirements
    • Include at least 3 non-functional requirements
    • Write acceptance criteria for each requirement
  3. Methodology Comparison

    • Create a comparison table of Waterfall, Scrum, and Kanban
    • List pros and cons of each
    • Identify project types best suited for each methodology

Intermediate Exercises

  1. Agile Sprint Simulation

    • Create a product backlog with 10 user stories
    • Plan a 2-week sprint with a team velocity of 20 points
    • Simulate the sprint with daily progress
    • Conduct a sprint retrospective
  2. Scrum Implementation

    • Implement the ScrumTeam and ScrumEvents classes
    • Add methods for tracking impediments
    • Create a burndown chart calculation
    • Implement sprint metrics (velocity, commitment reliability)
  3. Kanban Board

    • Implement a full Kanban board with WIP limits
    • Add methods to calculate lead time and cycle time
    • Implement a cumulative flow diagram
    • Create alerts when WIP limits are violated

Advanced Exercises

  1. Hybrid Methodology

    • Design a hybrid methodology combining Scrum and Kanban (Scrumban)
    • Define when to use sprints vs continuous flow
    • Create transition criteria between modes
    • Implement in code
  2. Project Simulation

    • Create a complete project simulation from requirements to deployment
    • Include random events (requirements changes, bugs, team changes)
    • Calculate success metrics (on time, on budget, quality)
    • Compare outcomes with different methodologies
  3. Estimation Accuracy

    • Implement multiple estimation techniques (story points, hours, t-shirt sizes)
    • Track actual vs estimated for simulated work
    • Calculate estimation accuracy over time
    • Implement learning to improve estimates
  4. Continuous Improvement

    • Create a system to track retrospective action items
    • Implement metrics to measure improvement
    • Build a knowledge base of lessons learned
    • Create recommendations for process improvements

Summary

The Software Development Lifecycle provides structure for building software:

  1. SDLC Phases: Planning, requirements, design, implementation, testing, deployment, maintenance
  2. Waterfall: Sequential, documentation-heavy, good for stable requirements
  3. Agile: Iterative, flexible, customer-focused, embraces change
  4. Scrum: Agile framework with sprints, roles (PO/SM/Dev), and ceremonies
  5. Kanban: Flow-based, visualize work, limit WIP, continuous delivery
  6. Choosing Methodology: Consider requirements stability, team size, complexity, and customer involvement

Key takeaways:

  • No single methodology fits all projects
  • Understanding principles matters more than rigid adherence
  • Continuous improvement is essential
  • Communication and collaboration are critical
  • Deliver value incrementally when possible

Next Reading

Continue to 03-design-patterns.md to learn about common software design patterns that solve recurring design problems.