Skip to content

Feature Prioritization

prism-roadmap includes two industry-standard prioritization frameworks that integrate with OpportunitySpec: RICE Scoring and the Kano Model.

Overview

Framework Purpose Output Best For
RICE Quantitative prioritization Numeric score Ranking features by ROI
Kano Qualitative classification Category Understanding customer impact

Both frameworks are embedded in OpportunitySpec to enable prioritization at the feature discovery stage.

RICE Scoring

RICE is a quantitative framework for prioritizing features based on four factors.

Reference: Intercom - RICE Scoring

Formula

Score = (Reach × Impact × Confidence) / Effort

Components

Component Description Range
Reach Number of users affected per time period Integer (users/quarter)
Impact Effect on each user massive (3x), high (2x), medium (1x), low (0.5x), minimal (0.25x)
Confidence Certainty in estimates high (100%), medium (80%), low (50%)
Effort Resources required Float (person-months)

Impact Levels

Level Multiplier Description
massive 3.0 Game-changing impact for every user
high 2.0 Significant positive impact
medium 1.0 Standard improvement
low 0.5 Minor benefit
minimal 0.25 Marginal improvement

Confidence Levels

Level Multiplier When to Use
high 1.0 (100%) Strong data, validated assumptions
medium 0.8 (80%) Some data, reasonable assumptions
low 0.5 (50%) Gut feel, limited data

Example Calculation

// Feature: Bulk export
score := prioritization.NewRICEScore(
    "bulk-export",
    1000,                        // Reach: 1000 users/quarter
    prioritization.ImpactHigh,   // Impact: 2x
    prioritization.ConfidenceHigh, // Confidence: 100%
    2.0,                         // Effort: 2 person-months
)
score.Calculate()
// Score = (1000 × 2.0 × 1.0) / 2.0 = 1000

RICEScore Fields

type RICEScore struct {
    FeatureID   string          `json:"featureId"`
    FeatureName string          `json:"featureName,omitempty"`

    // RICE components
    Reach      int             `json:"reach"`
    ReachUnit  string          `json:"reachUnit"`   // "quarter", "month"
    Impact     ImpactLevel     `json:"impact"`
    Confidence ConfidenceLevel `json:"confidence"`
    Effort     float64         `json:"effort"`
    EffortUnit string          `json:"effortUnit"`  // "person-months"

    // Calculated
    Score float64 `json:"score"`

    // Justifications
    ReachJustification      string `json:"reachJustification,omitempty"`
    ImpactJustification     string `json:"impactJustification,omitempty"`
    ConfidenceJustification string `json:"confidenceJustification,omitempty"`
    EffortJustification     string `json:"effortJustification,omitempty"`
}

Ranking Features

set := prioritization.NewRICEScoreSet()
set.Add(*prioritization.NewRICEScore("feat-1", 1000, ImpactHigh, ConfidenceHigh, 1))
set.Add(*prioritization.NewRICEScore("feat-2", 500, ImpactMassive, ConfidenceMedium, 3))
set.Add(*prioritization.NewRICEScore("feat-3", 2000, ImpactLow, ConfidenceLow, 0.5))

// Get top 2 features
top2 := set.TopN(2)

// Get rank of specific feature
rank := set.Rank("feat-2") // Returns 1-based rank

Kano Model

The Kano Model classifies features by their impact on customer satisfaction.

Reference: ProductPlan - Kano Model

Categories

Category Description Priority
Must-Be Basic expectation. Absence causes dissatisfaction, presence expected. 5 (highest)
Performance Linear satisfaction. More is better. 4
Attractive Delighter. Unexpected positive surprise. 3
Indifferent No significant impact on satisfaction. 1
Reverse Unwanted. Presence causes dissatisfaction. 0
Questionable Contradictory response, needs clarification. 0

Kano Questionnaire

The Kano Model uses paired questions:

  1. Functional question: "If the product HAS this feature, how do you feel?"
  2. Dysfunctional question: "If the product DOES NOT HAVE this feature, how do you feel?"

Response options:

  • like - I like it
  • expect - I expect it
  • neutral - I am neutral
  • tolerate - I can tolerate it
  • dislike - I dislike it

Evaluation Table

The response pair maps to a category:

                    | Dysfunctional Response
Functional Response | Like    | Expect  | Neutral | Tolerate | Dislike
--------------------|---------|---------|---------|----------|--------
Like                | Q       | A       | A       | A        | O
Expect              | R       | I       | I       | I        | M
Neutral             | R       | I       | I       | I        | M
Tolerate            | R       | I       | I       | I        | M
Dislike             | R       | R       | R       | R        | Q

Legend: M=Must-be, O=Performance, A=Attractive, I=Indifferent, R=Reverse, Q=Questionable

Classification Example

// If user LIKES having dark mode (functional)
// and is NEUTRAL about not having it (dysfunctional)
// → Category is ATTRACTIVE (delighter)

category := prioritization.ClassifyKano(
    prioritization.KanoLike,
    prioritization.KanoNeutral,
)
// category == KanoAttractive

KanoFeature Fields

type KanoFeature struct {
    FeatureID   string `json:"featureId"`
    FeatureName string `json:"featureName"`
    Description string `json:"description,omitempty"`

    // Questionnaire responses
    FunctionalResponse    KanoResponse `json:"functionalResponse"`
    DysfunctionalResponse KanoResponse `json:"dysfunctionalResponse"`

    // Classified category
    Category KanoCategory `json:"category"`

    // Coefficients (from aggregate analysis)
    SatisfactionCoefficient    float64 `json:"satisfactionCoefficient,omitempty"`
    DissatisfactionCoefficient float64 `json:"dissatisfactionCoefficient,omitempty"`

    // Response counts (for aggregate analysis)
    MustBeCount      int `json:"mustBeCount,omitempty"`
    PerformanceCount int `json:"performanceCount,omitempty"`
    AttractiveCount  int `json:"attractiveCount,omitempty"`
    IndifferentCount int `json:"indifferentCount,omitempty"`
}

Satisfaction Coefficients

When analyzing multiple respondents, calculate coefficients:

// Satisfaction: How much satisfaction increases WITH the feature
// Formula: (A + O) / (A + O + M + I)

// Dissatisfaction: How much satisfaction decreases WITHOUT the feature
// Formula: -1 * (O + M) / (A + O + M + I)

Example:

feature := prioritization.KanoFeature{
    FeatureID:        "dark-mode",
    AttractiveCount:  20,
    PerformanceCount: 30,
    MustBeCount:      40,
    IndifferentCount: 10,
}
feature.CalculateCoefficients()

// SatisfactionCoefficient = (20+30)/(20+30+40+10) = 0.5
// DissatisfactionCoefficient = -1*(30+40)/(100) = -0.7

Aggregate Analysis

analysis := prioritization.NewKanoAnalysis()

analysis.Add(KanoFeature{
    FeatureID:             "feature-1",
    FunctionalResponse:    KanoExpect,
    DysfunctionalResponse: KanoDislike,
}) // → Must-Be

analysis.Add(KanoFeature{
    FeatureID:             "feature-2",
    FunctionalResponse:    KanoLike,
    DysfunctionalResponse: KanoNeutral,
}) // → Attractive

// Filter by category
mustHaves := analysis.MustHaves()
delighters := analysis.Delighters()

// Sort by priority
analysis.SortByPriority()

// Get summary
summary := analysis.Summary()
// map[KanoCategory]int{"must-be": 1, "attractive": 1}

Integration with OpportunitySpec

OpportunitySpec includes both frameworks as optional fields:

type OpportunitySpec struct {
    // ... 12 boxes ...

    // Prioritization Frameworks
    RICE *prioritization.RICEScore   `json:"rice,omitempty"`
    Kano *prioritization.KanoFeature `json:"kano,omitempty"`
}

Setting RICE Score

spec := canvas.NewOpportunitySpec("mobile-app", "Mobile App Feature")

// Set RICE using convenience method
spec.SetRICE(1000, ImpactHigh, ConfidenceHigh, 2.0)

// Check RICE
if spec.HasRICE() {
    score := spec.GetRICEScore()
    fmt.Printf("RICE Score: %.0f\n", score)
}

Setting Kano Category

// Set Kano from questionnaire responses
spec.SetKano(KanoLike, KanoDislike)

// Check Kano
if spec.HasKano() {
    category := spec.GetKanoCategory()
    fmt.Printf("Kano: %s\n", category) // "performance"
}

// Convenience checks
if spec.IsMustHave() {
    // Prioritize this feature
}
if spec.IsDelighter() {
    // Consider for differentiation
}

Prioritization Summary

summary := spec.GetPrioritizationSummary()
fmt.Printf("RICE: %.0f, Kano: %s, Priority: %d\n",
    summary.RICEScore,
    summary.KanoCategory,
    summary.KanoPriority,
)

Decision Framework

When to Use RICE

  • Comparing multiple features for roadmap prioritization
  • Need quantitative justification for investment
  • Effort and reach estimates are reasonably reliable

When to Use Kano

  • Understanding customer expectations vs. delighters
  • Deciding between "must-have" and "nice-to-have"
  • Product-market fit validation

Combined Approach

Use both for comprehensive prioritization:

  1. Kano first: Classify features by customer impact
  2. RICE for tiebreakers: Within each Kano category, rank by RICE score
// Filter must-haves, then sort by RICE
mustHaves := analysis.MustHaves()
riceSet := prioritization.NewRICEScoreSet()
for _, f := range mustHaves {
    // Assume RICE scores are attached
    riceSet.Add(f.RICE)
}
topMustHave := riceSet.TopN(1)[0]

Priority Matrix

Kano Category RICE Score Action
Must-Be Any Implement first
Performance High (>1000) High priority
Performance Low (<500) Medium priority
Attractive High Differentiation opportunity
Attractive Low Nice to have
Indifferent Any Deprioritize
Reverse Any Do not implement

See Also