Planning

Post #5 in the How It Works series. We are building a task management app (simplified Trello) as our running example. Now it is time to plan the work.

1. Why Planning Matters

“Weeks of coding can save you hours of planning.” Every senior developer has lived through a project where the team jumped straight into code, only to realize weeks later they built the wrong thing, in the wrong order, with components that do not integrate.

Poor planning leads to:

  • Scope creep — Every meeting adds “one more feature” until the deadline is fantasy.
  • Missed deadlines — You cannot predict delivery if you do not know how much work exists.
  • Burnt-out teams — Constant context-switching and moving targets kill motivation.
  • Integration nightmares — Teams build components that do not fit together.

Planning forces the team to think through work before coding, surface risks early, and agree on priorities.

2. Break It Down

The most valuable planning activity is decomposition — breaking a large project into pieces small enough to understand, estimate, and assign.

Level Definition Example
Epic Major capability User Management
Story User-facing feature User can register with email
Task Developer work unit Create User JPA entity

Our task app epics:

Epic Key Stories
User Management Registration, login, password reset
Task CRUD Create, edit, delete, view, assign tasks
Board & Column Management Create board, add columns, drag-and-drop
Notifications Email on assignment, in-app alerts, daily digest

Each epic is independent enough for a different developer. Decomposition creates parallelism.

3. Estimate Effort

The goal is not precision — it is shared understanding of relative size.

  • Story points — Fibonacci (1, 2, 3, 5, 8, 13). Track velocity over time.
  • T-shirt sizing — S, M, L, XL. Fast for roadmap-level planning.
  • Time-based ranges — “2-5 days” not “3 days.” Ranges communicate uncertainty honestly.

Task CRUD estimates:

Story Points Time Range Notes
Create task 3 1-2 days Form + API endpoint
Edit task 3 1-2 days Reuse form component
Delete task 2 0.5-1 day Soft delete + confirmation
View task details 2 1-2 days Detail page + activity log
Assign task 5 2-4 days User search, permissions, notification

If a story estimates above 8 points, break it down further. Large estimates hide uncertainty.

4. Prioritize

Not everything ships in v1. The MoSCoW method forces explicit decisions:

Priority Meaning Our Features
Must Have Product does not work without it Registration, login, create/view tasks, basic board
Should Have Important but usable without it Edit, delete, assign tasks, drag-and-drop
Could Have Nice if time allows Email notifications, task comments
Won’t Have Out of scope for v1 Daily digest, Slack integration, mobile app

The “Won’t Have” category is the most important. Saying no explicitly prevents scope creep.

5. Sprint Planning

Plan in sprints — 1-2 week windows where the team commits to specific work. Only pull in what the team can finish. Teams operate at 60-70% capacity after meetings, reviews, and bugs.

Sample Sprint 1 (2-week sprint, 2 developers, ~30 points capacity):

Story Points Assignee Definition of Done
User registration 5 Dev A Register, confirmation email, stored in DB
User login/logout 5 Dev A JWT auth, session management
Create task 3 Dev B API + UI form, task persisted
View task list 3 Dev B Paginated list with status and assignee
Board with default columns 5 Dev B To Do, In Progress, Done columns
DB schema + scaffolding 5 Dev A Entities, migrations, CI pipeline

Total: 26 points. Four-point buffer for the unexpected work that will appear.

6. Technical Planning

Product planning is what to build. Technical planning is how. Agree on database schema, API contracts, and architecture decisions before writing application code.

Here is the planned Task entity in Java using JPA:

import jakarta.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "tasks")
public class Task {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false, length = 255)
    private String title;

    @Column(columnDefinition = "TEXT")
    private String description;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 20)
    private TaskStatus status;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, length = 10)
    private TaskPriority priority;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "assignee_id")
    private User assignee;

    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @Column(name = "updated_at", nullable = false)
    private LocalDateTime updatedAt;

    @PrePersist
    protected void onCreate() {
        this.createdAt = LocalDateTime.now();
        this.updatedAt = LocalDateTime.now();
    }

    @PreUpdate
    protected void onUpdate() {
        this.updatedAt = LocalDateTime.now();
    }
}

The same schema in Python using SQLAlchemy:

from sqlalchemy import Column, Integer, String, Text, Enum, ForeignKey, DateTime
from sqlalchemy.orm import relationship, DeclarativeBase
from datetime import datetime
import enum


class Base(DeclarativeBase):
    pass


class TaskStatus(enum.Enum):
    TODO = "TODO"
    IN_PROGRESS = "IN_PROGRESS"
    DONE = "DONE"


class TaskPriority(enum.Enum):
    LOW = "LOW"
    MEDIUM = "MEDIUM"
    HIGH = "HIGH"
    CRITICAL = "CRITICAL"


class Task(Base):
    __tablename__ = "tasks"

    id = Column(Integer, primary_key=True, autoincrement=True)
    title = Column(String(255), nullable=False)
    description = Column(Text, nullable=True)
    status = Column(Enum(TaskStatus), nullable=False, default=TaskStatus.TODO)
    priority = Column(Enum(TaskPriority), nullable=False, default=TaskPriority.MEDIUM)
    assignee_id = Column(Integer, ForeignKey("users.id"), nullable=True)
    created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
    updated_at = Column(DateTime, nullable=False, default=datetime.utcnow,
                        onupdate=datetime.utcnow)

    assignee = relationship("User", back_populates="tasks")

Both define the same contract: ID, title, description, status, priority, assignee foreign key, and timestamps. Writing models during planning forces alignment between frontend and backend before any application code exists.

7. Senior Tip

Eisenhower said it: “Plans are useless, but planning is everything.”

Your sprint plan will not survive contact with reality. A critical bug derails day three. A dependency team misses their deadline. A 3-point story turns into an 8. That is normal.

The value is the shared understanding built during the process. Decomposing together surfaces hidden dependencies. Estimating together lets the person who built something similar warn about edge cases. Prioritizing together aligns product and engineering on what “done” means.

The plan is a starting point. The process of planning is the actual product. Revisit every sprint. Update. Adjust scope. A plan that never changes is a plan nobody is looking at.




Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Leave a Reply

Your email address will not be published. Required fields are marked *