add takt time and simplify simulation getters

This commit is contained in:
Julien Calixte
2023-07-26 21:49:09 +02:00
parent 4af91102b8
commit 8d0d3dc7f9
6 changed files with 99 additions and 145 deletions

View File

@@ -7,22 +7,6 @@ import { onMounted } from 'vue'
const featureStore = useFeatureStore() const featureStore = useFeatureStore()
onMounted(() => featureStore.initBoard()) onMounted(() => featureStore.initBoard())
const pullAndProblemSolving20Percent = () => {
if (featureStore.meta.totalDays % 5 === 0) {
featureStore.nextDay('pull-dps')
} else {
featureStore.nextDay('pull')
}
}
const pushAndProblemSolving20Percent = () => {
if (featureStore.meta.totalDays % 5 === 0) {
featureStore.nextDay('push-dps')
} else {
featureStore.nextDay('push')
}
}
</script> </script>
<template> <template>
@@ -31,17 +15,16 @@ const pushAndProblemSolving20Percent = () => {
{{ featureStore.features.length }} features | mean complexity : {{ featureStore.features.length }} features | mean complexity :
{{ featureStore.meanComplexity }} | mean lead time : {{ featureStore.meanComplexity }} | mean lead time :
{{ featureStore.meanLeadTime }} days | Total days: {{ featureStore.meanLeadTime }} days | Total days:
{{ featureStore.meta.totalDays }} {{ featureStore.meta.totalDays }} | Team work experience:
{{ featureStore.meta.teamWorkExperience }}
</div> </div>
<div class="row">
New feature live every {{ featureStore.taktTime }} days.
</div>
<div class="row">Strategy of the day:</div>
<div class="row"> <div class="row">
<button @click="featureStore.nextDay('push')">push system</button> <button @click="featureStore.nextDay('push')">push system</button>
<button @click="featureStore.nextDay('pull')">pull system</button> <button @click="featureStore.nextDay('pull')">pull system</button>
<button @click="pushAndProblemSolving20Percent">
push and problem solving
</button>
<button @click="pullAndProblemSolving20Percent">
pull and problem solving
</button>
<button @click="featureStore.nextDay('problem-solving')"> <button @click="featureStore.nextDay('problem-solving')">
problem solving problem solving
</button> </button>

View File

@@ -3,7 +3,13 @@ import { FeatureStep } from '@/modules/feature/feature-steps'
import { features as initialFeatures } from '@/modules/feature/feature.fixture' import { features as initialFeatures } from '@/modules/feature/feature.fixture'
import { Strategy } from '@/modules/lean/strategy' import { Strategy } from '@/modules/lean/strategy'
import { FeatureState } from '@/store-type' import { FeatureState } from '@/store-type'
import { getMean, pickRandomElement, popNElement, shuffleArray } from '@/utils' import {
getMean,
pickRandomElement,
popNElement,
shuffleArray,
sumElements
} from '@/utils'
const MAX_FEATURES = 200 const MAX_FEATURES = 200
const HARD_STOP = 5000 const HARD_STOP = 5000
@@ -11,15 +17,15 @@ const HARD_STOP = 5000
const hasQualityIssue = ({ const hasQualityIssue = ({
complexity, complexity,
tasksInParallel, tasksInParallel,
daysWithProblemSolving teamWorkExperience
}: { }: {
complexity: number complexity: number
tasksInParallel: number tasksInParallel: number
daysWithProblemSolving: number teamWorkExperience: number
}): boolean => { }): boolean => {
const qualityProbability = getQualityProbability( const qualityProbability = getQualityProbability(
complexity, complexity,
daysWithProblemSolving teamWorkExperience
) )
const multiplicator = getOverburdenMultiplicator(tasksInParallel) const multiplicator = getOverburdenMultiplicator(tasksInParallel)
@@ -51,14 +57,14 @@ export const getFeaturesForNextDay = ({
initialStep, initialStep,
steps, steps,
strategy, strategy,
daysWithProblemSolving teamWorkExperience
}: { }: {
backlog: Feature[] backlog: Feature[]
features: Feature[] features: Feature[]
steps: FeatureStep[] steps: FeatureStep[]
initialStep: number initialStep: number
strategy: Strategy | 'problem-solving' strategy: Strategy | 'problem-solving'
daysWithProblemSolving: number teamWorkExperience: number
}): [Feature[], Feature[]] => { }): [Feature[], Feature[]] => {
features features
.filter((feature) => feature.step > 0 || feature.status === 'doing') .filter((feature) => feature.step > 0 || feature.status === 'doing')
@@ -107,7 +113,7 @@ export const getFeaturesForNextDay = ({
tasksInParallel: features.filter( tasksInParallel: features.filter(
(f) => f.status === 'doing' && f.step === feature.step (f) => f.status === 'doing' && f.step === feature.step
).length, ).length,
daysWithProblemSolving teamWorkExperience
}) })
) { ) {
feature.step = Math.min(4, feature.step + 1) feature.step = Math.min(4, feature.step + 1)
@@ -182,7 +188,7 @@ const getOverburdenMultiplicator = (tasksInParallel: number) => {
const getQualityProbability = ( const getQualityProbability = (
complexity: number, complexity: number,
daysWithProblemSolving: number teamWorkExperience: number
) => { ) => {
let probabilityOfGoodQuality = 1 let probabilityOfGoodQuality = 1
@@ -206,11 +212,14 @@ const getQualityProbability = (
// team learning // team learning
probabilityOfGoodQuality = probabilityOfGoodQuality =
probabilityOfGoodQuality + (1.2 * daysWithProblemSolving) / 100 probabilityOfGoodQuality + (1.2 * teamWorkExperience) / 100
return probabilityOfGoodQuality return probabilityOfGoodQuality
} }
const isFeatureDone = (feature: Feature) =>
feature.step === 0 && feature.status === 'done'
export const nextDay = ( export const nextDay = (
state: FeatureState, state: FeatureState,
strategy: Strategy | 'problem-solving' strategy: Strategy | 'problem-solving'
@@ -218,7 +227,10 @@ export const nextDay = (
state.meta.totalDays++ state.meta.totalDays++
if (strategy === 'problem-solving') { if (strategy === 'problem-solving') {
state.meta.daysWithProblemSolving++ const hasTeamLearned = Math.random() > 0.25
if (hasTeamLearned) {
state.meta.teamWorkExperience++
}
} else { } else {
state.meta.strategy[strategy]++ state.meta.strategy[strategy]++
} }
@@ -229,16 +241,21 @@ export const nextDay = (
steps: state.steps, steps: state.steps,
initialStep: state.steps[0].stepIndex, initialStep: state.steps[0].stepIndex,
strategy, strategy,
daysWithProblemSolving: state.meta.daysWithProblemSolving teamWorkExperience: state.meta.teamWorkExperience
}) })
state.backlog = backlog state.backlog = backlog
state.features = features state.features = features
const featuresDone = sumElements(state.meta.featuresDonePerDay) ?? 0
const featuresDoneNextDay = features.filter(isFeatureDone).length
state.meta.featuresDonePerDay.push(featuresDoneNextDay - featuresDone)
return state return state
} }
export const isProjectFinished = (features: Feature[]) => export const isProjectFinished = (features: Feature[]) =>
features.every((feature) => feature.step === 0 && feature.status === 'done') features.every(isFeatureDone)
export const getMeanComplexity = (features: Feature[]) => { export const getMeanComplexity = (features: Feature[]) => {
return getMean(features.map((feature) => feature.complexity)) return getMean(features.map((feature) => feature.complexity))

View File

@@ -10,12 +10,13 @@ import {
import { featureSteps } from '@/modules/feature/feature-steps' import { featureSteps } from '@/modules/feature/feature-steps'
import { Strategy } from '@/modules/lean/strategy' import { Strategy } from '@/modules/lean/strategy'
import { FeatureState, Meta } from '@/store-type' import { FeatureState, Meta } from '@/store-type'
import { clone } from '@/utils' import { sumElements } from '@/utils'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
const resetMeta = (): Meta => ({ const resetMeta = (): Meta => ({
totalDays: 0, totalDays: 0,
daysWithProblemSolving: 0, teamWorkExperience: 0,
featuresDonePerDay: [],
strategy: { strategy: {
push: 0, push: 0,
pull: 0, pull: 0,
@@ -32,18 +33,19 @@ export const useFeatureStore = defineStore('feature', {
meta: resetMeta() meta: resetMeta()
}), }),
actions: { actions: {
// set the number of feature we want
async initBoard() { async initBoard() {
this.backlog = newBacklog() this.backlog = newBacklog()
this.steps = featureSteps this.steps = featureSteps
this.features = initBoard(clone(this.steps), clone(this.backlog)) this.features = initBoard(this.steps, this.backlog)
this.backlog = this.backlog.filter( this.backlog = this.backlog.filter(
(l) => !this.features.find((f) => f.name === l.name) (l) => !this.features.find((f) => f.name === l.name)
) )
this.meta = resetMeta() this.meta = resetMeta()
}, },
async nextDay(strategy: Strategy) { async nextDay(strategy: Strategy | 'problem-solving') {
const newState = nextDay(clone(this.$state), strategy) const newState = nextDay(this.$state, strategy)
this.backlog = newState.backlog this.backlog = newState.backlog
this.meta = newState.meta this.meta = newState.meta
@@ -54,6 +56,10 @@ export const useFeatureStore = defineStore('feature', {
meanComplexity: (state) => getMeanComplexity(state.features), meanComplexity: (state) => getMeanComplexity(state.features),
meanLeadTime: (state) => getMeanLeadTime(state.features), meanLeadTime: (state) => getMeanLeadTime(state.features),
meanQualityIssue: (state) => getMeanQualityIssue(state.features), meanQualityIssue: (state) => getMeanQualityIssue(state.features),
taktTime: (state) =>
(
state.meta.totalDays / sumElements(state.meta.featuresDonePerDay)
).toFixed(2) ?? 0,
featuresGroupedByStep: (state) => { featuresGroupedByStep: (state) => {
const groupedByStep: Record<number, Feature[]> = {} const groupedByStep: Record<number, Feature[]> = {}

View File

@@ -1,7 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { Strategy } from '@/modules/lean/strategy'
import { useSimulationStore } from '@/modules/simulation/simulation-store' import { useSimulationStore } from '@/modules/simulation/simulation-store'
const simulationStore = useSimulationStore() const simulationStore = useSimulationStore()
const strategies: Strategy[] = ['push', 'pull', 'push-dps', 'pull-dps']
</script> </script>
<template> <template>
@@ -16,56 +19,35 @@ const simulationStore = useSimulationStore()
<thead> <thead>
<tr> <tr>
<th>#</th> <th>#</th>
<th>push</th> <th class="numeric">push</th>
<th>pull</th> <th class="numeric">pull</th>
<th>pull and DPS</th> <th class="numeric">pull and DPS</th>
<th>push and DPS</th> <th class="numeric">push and DPS</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>lead time</td> <td>lead time</td>
<td class="numeric"> <td class="numeric" v-for="strategy in strategies" :key="strategy">
{{ simulationStore.meanPushLeadTime }} {{ simulationStore.meanLeadTime(strategy) }}
</td> </td>
<td class="numeric"> </tr>
{{ simulationStore.meanPullLeadTime }} <tr>
</td> <td>takt time</td>
<td class="numeric"> <td class="numeric" v-for="strategy in strategies" :key="strategy">
{{ simulationStore.meanPullDPSLeadTime }} {{ simulationStore.meanTaktTime(strategy) }}
</td>
<td class="numeric">
{{ simulationStore.meanPushDPSLeadTime }}
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Complexity</td> <td>Complexity</td>
<td class="numeric"> <td class="numeric" v-for="strategy in strategies" :key="strategy">
{{ simulationStore.meanPushComplexity }} {{ simulationStore.meanComplexity(strategy) }}
</td>
<td class="numeric">
{{ simulationStore.meanPullComplexity }}
</td>
<td class="numeric">
{{ simulationStore.meanPullDPSComplexity }}
</td>
<td class="numeric">
{{ simulationStore.meanPushDPSComplexity }}
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Quality issue</td> <td>Quality issue</td>
<td class="numeric"> <td class="numeric" v-for="strategy in strategies" :key="strategy">
{{ simulationStore.meanPushQuality }} {{ simulationStore.meanQuality(strategy) }}
</td>
<td class="numeric">
{{ simulationStore.meanPullQuality }}
</td>
<td class="numeric">
{{ simulationStore.meanPullDPSQuality }}
</td>
<td class="numeric">
{{ simulationStore.meanPushDPSQuality }}
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -76,9 +58,13 @@ const simulationStore = useSimulationStore()
<style scoped lang="scss"> <style scoped lang="scss">
.simulation-dashboard { .simulation-dashboard {
color: var(--primary-color); color: var(--primary-color);
table {
padding: 1rem;
}
} }
td.numeric { .numeric {
text-align: right; text-align: right;
} }
</style> </style>

View File

@@ -1,13 +1,14 @@
import { featureSteps } from '@/modules/feature/feature-steps' import { featureSteps } from '@/modules/feature/feature-steps'
import { Strategy } from '@/modules/lean/strategy' import { Strategy } from '@/modules/lean/strategy'
import { Dashboard, Meta } from '@/store-type' import { Dashboard, Meta } from '@/store-type'
import { getRound } from '@/utils' import { getRound, sumElements } from '@/utils'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
// Get features done per day to plot it // Get features done per day to plot it
type Mean = { type Mean = {
leadTimeSum: number leadTimeSum: number
taktTimeSum: number
complexitySum: number complexitySum: number
qualityIssueSum: number qualityIssueSum: number
simulations: number simulations: number
@@ -22,6 +23,7 @@ type State = {
const newMean = (): Mean => ({ const newMean = (): Mean => ({
leadTimeSum: 0, leadTimeSum: 0,
taktTimeSum: 0,
complexitySum: 0, complexitySum: 0,
qualityIssueSum: 0, qualityIssueSum: 0,
simulations: 0 simulations: 0
@@ -33,7 +35,8 @@ const instance = new ComlinkWorker<typeof import('../feature/feature-board')>(
const resetMeta = (): Meta => ({ const resetMeta = (): Meta => ({
totalDays: 0, totalDays: 0,
daysWithProblemSolving: 0, teamWorkExperience: 0,
featuresDonePerDay: [],
strategy: { strategy: {
push: 0, push: 0,
pull: 0, pull: 0,
@@ -95,6 +98,10 @@ export const useSimulationStore = defineStore('dashboard', {
this.newDashboard(dashboard) this.newDashboard(dashboard)
this.mean[strategy].leadTimeSum += dashboard.analysis.meanLeadTime this.mean[strategy].leadTimeSum += dashboard.analysis.meanLeadTime
// todo: set directly the number of features
this.mean[strategy].taktTimeSum +=
dashboard.meta.totalDays /
sumElements(dashboard.meta.featuresDonePerDay)
this.mean[strategy].complexitySum += dashboard.analysis.meanComplexity this.mean[strategy].complexitySum += dashboard.analysis.meanComplexity
this.mean[strategy].qualityIssueSum += dashboard.analysis.meanQualityIssue this.mean[strategy].qualityIssueSum += dashboard.analysis.meanQualityIssue
this.mean[strategy].simulations++ this.mean[strategy].simulations++
@@ -115,71 +122,25 @@ export const useSimulationStore = defineStore('dashboard', {
} }
}, },
getters: { getters: {
meanPushLeadTime: (state) => { meanLeadTime: (state) => (strategy: Strategy) =>
return getRound(state.mean.push.leadTimeSum, state.mean.push.simulations) getRound(
}, state.mean[strategy].leadTimeSum,
meanPullLeadTime: (state) => { state.mean[strategy].simulations
return getRound(state.mean.pull.leadTimeSum, state.mean.pull.simulations) ),
}, meanTaktTime: (state) => (strategy: Strategy) =>
meanPullDPSLeadTime: (state) => { getRound(
return getRound( state.mean[strategy].taktTimeSum,
state.mean['pull-dps'].leadTimeSum, state.mean[strategy].simulations
state.mean['pull-dps'].simulations ),
meanComplexity: (state) => (strategy: Strategy) =>
getRound(
state.mean[strategy].complexitySum,
state.mean[strategy].simulations
),
meanQuality: (state) => (strategy: Strategy) =>
getRound(
state.mean[strategy].qualityIssueSum,
state.mean[strategy].simulations
) )
},
meanPushDPSLeadTime: (state) => {
return getRound(
state.mean['push-dps'].leadTimeSum,
state.mean['push-dps'].simulations
)
},
meanPushComplexity: (state) => {
return getRound(
state.mean.push.complexitySum,
state.mean.push.simulations
)
},
meanPullComplexity: (state) => {
return getRound(
state.mean.pull.complexitySum,
state.mean.pull.simulations
)
},
meanPullDPSComplexity: (state) => {
return getRound(
state.mean['pull-dps'].complexitySum,
state.mean['pull-dps'].simulations
)
},
meanPushDPSComplexity: (state) => {
return getRound(
state.mean['push-dps'].complexitySum,
state.mean['push-dps'].simulations
)
},
meanPushQuality: (state) => {
return getRound(
state.mean.push.qualityIssueSum,
state.mean.push.simulations
)
},
meanPullQuality: (state) => {
return getRound(
state.mean.pull.qualityIssueSum,
state.mean.pull.simulations
)
},
meanPullDPSQuality: (state) => {
return getRound(
state.mean['pull-dps'].qualityIssueSum,
state.mean['pull-dps'].simulations
)
},
meanPushDPSQuality: (state) => {
return getRound(
state.mean['push-dps'].qualityIssueSum,
state.mean['push-dps'].simulations
)
}
} }
}) })

View File

@@ -4,8 +4,9 @@ import { Strategy } from '@/modules/lean/strategy'
export type Meta = { export type Meta = {
totalDays: number totalDays: number
daysWithProblemSolving: number teamWorkExperience: number
strategy: Record<Strategy, number> strategy: Record<Strategy, number>
featuresDonePerDay: number[]
} }
export type Analysis = { export type Analysis = {