# Prioritize with Python

My last prioritization meeting

The hardest part of building products isn't deciding what to build. It's deciding what to build first.

Every product manager faces the reality of unlimited ideas, constrained resources, and stakeholders asking for clarity on what's coming next. The usual response is a flurry of prioritization meetings and endless debates about what matters most and how to sequence it.

But here's what I've learned after years of wrestling with roadmaps - the thinking part and the sequencing part are fundamentally different problems. When you conflate them, you create unnecessary complexity and yield worse decisions.

## The Hidden Structure of Prioritization

Most product teams approach prioritization as a single, monolithic problem. This is a mistake. The process naturally breaks into two distinct phases, each requiring different mental models:

Ranking answers the question of what should we build, all constraints aside.

Sequencing determines the optimal order given our priorities and dependencies.

The first is an art. The second is a science.

Ranking requires judgment, intuition, and deep understanding of customer needs. It's where product sense matters most. You can't automate good judgment any more than you can automate taste.

But sequencing is mechanical. Once you know what you want to build and understand your constraints and dependencies, determining the optimal order follows predictable rules. Dependencies create sequences, resource constraints create trade-offs, and time pressure creates urgency.

The problem is that most teams manually recalculate sequences every time priorities shift. This creates two problems - it's time-consuming, and it's error-prone. Every manual recalculation becomes an opportunity for sub-optimization.

## Automating the Mechanical

After watching too many teams struggle with this problem, I built a simple Python script that automates the sequencing process. Feed it your ranked priorities and dependency relationships, and it outputs the optimal development sequence.

The algorithm is straightforward - prioritize high-value, low-dependency features first. When dependencies exist, sequence them appropriately. When multiple features have equal priority and dependency profiles, the original ranking becomes the tiebreaker.

The script reads a CSV file containing features, their relative priorities, and dependencies between them. It then applies a consistent set of rules to determine the optimal sequence.

The script's sequencing logic

The beauty lies not in the complexity of the algorithm (it's quite simple) but in its consistency. Humans make different decisions based on mood, recent conversations, and cognitive biases. The script makes the same decision every time when given the same inputs.

The sequencing algorithm in action

The output includes two critical pieces of information - the optimal sequence and how much each feature's position changed from the original ranking. This change metric reveals something important. It shows where dependencies and constraints significantly alter your intuitive ordering.

## The Implementation

The script requires basic Python knowledge and takes less than five minutes to run. You'll need:

  • Python 3 with numpy and pandas
  • A CSV file with your features, priorities, and dependencies
  • Basic command line familiarity

If you lack technical skills, you have two options. First, stick with manual sequencing using the flowchart above (it's perfectly workable for smaller roadmaps). Second, invest time in learning basic programming skills. This investment pays dividends far beyond roadmap management.

The execution is simple:

  1. Save the script as prioritize.py
  2. Create your input CSV with features, priorities, and dependencies
  3. Run: python3 prioritize.py --source your_features.csv
  4. Review the optimized sequence

The script outputs results to the command line by default, but you can save to a new CSV file using the --output flag.

## Beyond Efficiency

This approach delivers more than time savings. It surfaces hidden insights about your roadmap's structure. When the script significantly reorders your intuitive sequence, it's highlighting dependency chains you may have missed or underestimated.

It also enables rapid scenario planning. Change a few priority scores or add new dependencies, rerun the script, and see how your roadmap shifts. This makes quarterly planning discussions more productive. Instead of debating sequences, you can focus on the harder questions of relative priority and strategic direction.

The script isn't perfect. It doesn't account for varying development times or team capacity constraints, and it assumes features can't be parallelized. Real roadmaps are messier than CSV files.

But perfect is the enemy of good. The script handles 80% of sequencing decisions automatically, freeing you to focus on the 20% that require human judgment. In product management, this is a significant win.

The code is below. Use it, modify it, improve it. Your roadmap (and your sanity) will thank you.

# The Script

1import pandas as pd
2import numpy as np
3import argparse
4
5def main():
6    # Set up the command line parser
7    parser = argparse.ArgumentParser(description="Prioritize features based on dependencies")
8    parser.add_argument("-i", "--input", help="Source CSV file", required=True)
9    parser.add_argument("-o", "--output", help="Output CSV file", required=False)
10    args = parser.parse_args()
11
12    # Load, process, and output the feature list
13    df = load_feature_list(args.input)
14    df = process_feature_list(df)
15    output_feature_list(df, args.output)
16
17
18def load_feature_list(filename):
19    """Load features from CSV and prepare for processing"""
20    df = pd.read_csv(filename)
21    df['sorted_priority'] = np.nan
22    df['dependency_count'] = np.nan
23
24    # Convert dependency strings to lists
25    df['temp_dependencies'] = df['dependencies'].apply(split_dependencies)
26    return df
27
28
29def process_feature_list(df):
30    """Process features to determine optimal sequence"""
31    remaining_features = len(df)
32
33    while remaining_features > df['sorted_priority'].count():
34        # Calculate dependency counts and sort
35        df['dependency_count'] = df['temp_dependencies'].apply(len)
36        df.sort_values(by=['sorted_priority', 'dependency_count', 'priority'],
37                      inplace=True, na_position='first')
38        df.reset_index(drop=True, inplace=True)
39
40        # Assign next priority to the best available feature
41        df.at[0, 'sorted_priority'] = get_next_priority(df)
42
43        # Remove completed feature from other dependencies
44        completed_feature = df.at[0, 'id']
45        df = remove_from_dependencies(df, completed_feature)
46
47    return df
48
49
50def output_feature_list(df, output_file=None):
51    """Output the prioritized feature list"""
52    # Calculate priority changes
53    df['priority_change'] = df['priority'] - df['sorted_priority']
54
55    # Sort for final output
56    df.sort_values(by=['sorted_priority'], inplace=True)
57
58    # Select relevant columns
59    output_df = df[['id', 'name', 'sorted_priority', 'priority',
60                   'dependencies', 'priority_change']].copy()
61
62    print(output_df.to_string(index=False))
63
64    if output_file:
65        output_df.to_csv(output_file, index=False)
66        print(f"\nResults saved to {output_file}")
67
68
69def split_dependencies(dependency_string):
70    """Convert dependency string to list"""
71    if pd.isnull(dependency_string):
72        return []
73    if isinstance(dependency_string, str):
74        return [dep.strip() for dep in dependency_string.split(',') if dep.strip()]
75    return []
76
77
78def get_next_priority(df):
79    """Get the next available priority number"""
80    max_priority = df['sorted_priority'].max()
81    return 1 if pd.isnull(max_priority) else max_priority + 1
82
83
84def remove_from_dependencies(df, completed_feature):
85    """Remove completed feature from all dependency lists"""
86    for idx in range(len(df)):
87        current_deps = df.at[idx, 'temp_dependencies']
88        df.at[idx, 'temp_dependencies'] = [dep for dep in current_deps
89                                          if dep != completed_feature]
90    return df
91
92
93if __name__ == "__main__":
94    main()

Note: This is a simplified model. It doesn't account for development time variations or parallel work streams. If the output doesn't align with your team's constraints, adapt accordingly. The goal is progress, not perfection.