ЁЯПл The SchoolтА║ЁЯЫдя╕П Platform EngineeringтА║ЁЯУК рдзрдбрд╛ 11 тАФ Platform рдЪреЗ рдореЛрдЬрдорд╛рдк: рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдЪрд╛ рдЙрдкрдпреЛрдЧ рд╣реЛрддреЛрдп рдХрд╛?
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯУК рдзрдбрд╛ 11 тАФ Platform рдЪреЗ рдореЛрдЬрдорд╛рдк: рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдЪрд╛ рдЙрдкрдпреЛрдЧ рд╣реЛрддреЛрдп рдХрд╛?

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 11 ┬╖ рдорд╛рдЧреАрд▓: lesson-10-scorecards ┬╖ рдкреБрдвреАрд▓: lesson-12-developer-portal


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рдзрдбреЗ 01тАУ10, рдЖрдгрд┐ platform рдорджрдд рдХрд░рддреЛ рдХреА рдирд╛рд╣реА рд╣реЗ рдХрд╕реЗ рдореЛрдЬрд╛рдпрдЪреЗ. Deployments рдЪреНрдпрд╛ рдпрд╛рджреАрддреВрди рдореЛрдЬрд▓реЗрд▓реНрдпрд╛ рдЪрд╛рд░ DORA keys, рдирд╡реНрдпрд╛ team рд╕рд╛рдареА рдкрд╣рд┐рд▓реНрдпрд╛ deploy рдкрд░реНрдпрдВрддрдЪрд╛ рд╡реЗрд│, рдЖрдХрдбреНрдпрд╛рдВрддреВрди рд╕реБрдЯрдгрд╛рд▒реНрдпрд╛ рднрд╛рдЧрд╛рдВрд╕рд╛рдареА SPACE framework тАФ рдЖрдгрд┐ рдзреНрдпреЗрдп рдмрдирд╡рд▓реЗрд▓рд╛ metric рдХрд╕рд╛ рдЕрд░реНрдерд╣реАрди рд╣реЛрддреЛ рдпрд╛рдЪреЗ рдкреНрд░рд╛рддреНрдпрдХреНрд╖рд┐рдХ. dora() idp/guard.py рдордзреНрдпреЗ, measure() idp/demo.py рдордзреНрдпреЗ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рдореБрдЦреНрдпрд╛рдзреНрдпрд╛рдкрд┐рдХрд╛ рджреАрдкрд┐рдХрд╛рд▓рд╛ рд╡рд┐рдЪрд╛рд░рддрд╛рдд: "рддреВ kits, catalogue рдЖрдгрд┐ рдХрд┐рд▓реНрд▓реНрдпрд╛рдВрдЪреЗ рдХрдкрд╛рдЯ рдмрдирд╡рд▓реЗрд╕. рддреНрдпрд╛рдЪрд╛ рдЙрдкрдпреЛрдЧ рдЭрд╛рд▓рд╛ рдХрд╛?"

рджреАрдкрд┐рдХрд╛ science рд╡рд┐рднрд╛рдЧрд╛рдиреЗ рдЖрдкрд▓реНрдпрд╛ рд╡рд░реНрдЧрдЦреЛрд▓реНрдпрд╛рдВрдордзреНрдпреЗ рдХреЗрд▓реЗрд▓реЗ рдмрджрд▓ рдкрд╛рд╣рддреЗ, kits рдЪреНрдпрд╛ рдЖрдзреАрдЪреЗ рдЪрд╛рд░ рдЖрдард╡рдбреЗ рдЖрдгрд┐ рдирдВрддрд░рдЪреЗ рдЪрд╛рд░ рдЖрдард╡рдбреЗ:

рдордЧ рдореБрдЦреНрдпрд╛рдзреНрдпрд╛рдкрд┐рдХрд╛ рдореНрд╣рдгрддрд╛рдд: "рдЫрд╛рди! рдЖрдЬрдкрд╛рд╕реВрди рдкреНрд░рддреНрдпреЗрдХ рд╡рд┐рднрд╛рдЧрд╛рдиреЗ рдЖрдард╡рдбреНрдпрд╛рд▓рд╛ 9 рдмрджрд▓ рдХрд░рд╛рдпрд▓рд╛рдЪ рд╣рд╡реЗрдд." ЁЯУИ рдкреБрдврдЪреНрдпрд╛ рдЖрдард╡рдбреНрдпрд╛рдд maths рд╡рд┐рднрд╛рдЧ 9 рд╕рд╛рдВрдЧрддреЛ... рддреЛрдЪ рдмрджрд▓ рддреАрди рд╡реЗрд│рд╛ рдореЛрдЬреВрди. рдЖрдХрдбрд╛ рд╡рд╛рдврд▓рд╛. рд╡рд┐рджреНрдпрд╛рд░реНрдереНрдпрд╛рдВрд╕рд╛рдареА рдХрд╛рд╣реАрдЪ рд╕реБрдзрд╛рд░рд▓реЗ рдирд╛рд╣реА.

рдореНрд╣рдгреВрди рджреАрдкрд┐рдХрд╛ рд╢рд┐рдХреНрд╖рд┐рдХрд╛рдВрдирд╛ рд╡рд┐рдЪрд╛рд░рддреЗрд╣реА: "рддреБрдордЪреЗ рдХрд╛рдо рд╕реЛрдкреЗ рдЭрд╛рд▓реЗ рдХрд╛? рддреБрдореНрд╣реА рдХрдореА рд╡реЗрд│рд╛ рдЕрдбрдХрддрд╛ рдХрд╛?" рдЖрдХрдбреЗ рдЖрдгрд┐ рдЙрддреНрддрд░реЗ рдорд┐рд│реВрди рд╕рддреНрдп рд╕рд╛рдВрдЧрддрд╛рдд. рдлрдХреНрдд рдЖрдХрдбреЗ рд╡рд╛рдХрд╡рддрд╛ рдпреЗрддрд╛рдд.

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    d["ЁЯУж deployments<br/>commit hour ┬╖ deploy hour ┬╖<br/>failed? ┬╖ restored hour"] --> k["ЁЯФС four DORA keys"]
    k --> f["deploys/week<br/>1.0 тЖТ 3.0"]
    k --> lt["lead time<br/>150 h тЖТ 5 h"]
    k --> cfr["change failure rate<br/>50% тЖТ 8%"]
    k --> rt["recovery time<br/>23 h тЖТ 2 h"]
    g["тЪая╕П count re-runs:<br/>9.0/week, nothing better"] -.-> f
    sp["ЁЯЧгя╕П SPACE: ask people too"] --> k

ЁЯЧ║я╕П рдХрд╛рдврд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/platform-engineering/lesson-diagrams.html#l11

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг platform рд╕рд╛рдареА рд▓реЛрдХрд╛рдВрдЪреА рдПрдХ team рд▓рд╛рдЧрддреЗ, рдЖрдгрд┐ рддреНрдпрд╛рддреВрди рдХрд╛рдп рдорд┐рд│рд╛рд▓реЗ рд╣реЗ рдХреЛрдгреАрддрд░реА рд╡рд┐рдЪрд╛рд░рдгрд╛рд░рдЪ. "рдЬрд▓рдж рд╡рд╛рдЯрддреЗ" рд╣реЗ budget meeting рдордзреНрдпреЗ рдЯрд┐рдХрдд рдирд╛рд╣реА; рдЦрд▒реНрдпрд╛ deployments рдордзрд▓реЗ, рдЖрдзреА рдЖрдгрд┐ рдирдВрддрд░рдЪреА рддреБрд▓рдирд╛ рдХреЗрд▓реЗрд▓реЗ рдЪрд╛рд░ рдЖрдХрдбреЗ рдЕрдиреЗрдХрджрд╛ рдЯрд┐рдХрддрд╛рдд. рдкрдг рддреЗрдЪ рдЖрдХрдбреЗ рдПрдХрджрд╛ рдзреНрдпреЗрдп рдмрдирд▓реЗ рдХреА рддреЗ рдЬреНрдпрд╛ рд╡рд╛рдЧрдгреБрдХреАрдЪреЗ рдирд┐рд░реАрдХреНрд╖рдг рдХрд░рд╛рдпрдЪреЗ рд╣реЛрддреЗ рддреАрдЪ рдмрджрд▓рддрд╛рдд. рдореНрд╣рдгреВрди platform рдЪрд╛ рдкрд░рд┐рдгрд╛рдо teams рд╡рд░ рдореЛрдЬрд╛, рдЖрдХрдбреЗ teams рд╕реЛрдмрдд рд╡рд╛рдЯрд╛, рдЖрдгрд┐ рддреНрдпрд╛рдВрдирд╛ рд▓реЛрдХрд╛рдВрдирд╛ рд╡рд┐рдЪрд╛рд░рдгреНрдпрд╛рдЪреА рдЬреЛрдб рджреНрдпрд╛.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

idp/guard.py рдордзреАрд▓ dora(deploys, weeks) deployments (commit_hour, deploy_hour, failed, restored_hour) рдореНрд╣рдгреВрди рдШреЗрддреЛ. рддреЛ рджрд░ рдЖрдард╡рдбреНрдпрд╛рдЪреЗ deployments, рддрд╛рд╕рд╛рдВрдордзрд▓рд╛ median lead time, рдкреВрд░реНрдг рдЯрдХреНрдХреНрдпрд╛рдВрдд change failure rate, рдЖрдгрд┐ failed рдЭрд╛рд▓реЗрд▓реНрдпрд╛рдВрдЪрд╛ median recovery time рдкрд░рдд рджреЗрддреЛ. (DORA рдЪреЗ surveys ranges рд╡рд┐рдЪрд╛рд░рддрд╛рдд; рд╕реНрд╡рддрдГрдЪреНрдпрд╛ data рдЪрд╛ рд╕рд╛рд░рд╛рдВрд╢ рдХрд╛рдврдгреНрдпрд╛рдЪрд╛ median рд╣рд╛ рдПрдХ рдпреЛрдЧреНрдп рдорд╛рд░реНрдЧ рдЖрд╣реЗ.) idp/demo.py рдордзреАрд▓ measure() science рдЪреЗ рдЖрдзреАрдЪреЗ 4 рдЖрдард╡рдбреЗ (BEFORE, 4 deploys) рдЖрдгрд┐ рдирдВрддрд░рдЪреЗ (AFTER, 12 deploys) рдпрд╛рдВрдЪреА рддреБрд▓рдирд╛ рдХрд░рддреЛ, рдзрдбрд╛ 01 рдЪреНрдпрд╛ рд░рд╛рдВрдЧреЗрд╡рд░реВрди рдкрд╣рд┐рд▓реНрдпрд╛ deploy рдкрд░реНрдпрдВрддрдЪреНрдпрд╛ рд╡реЗрд│реЗрдЪрд╛ рдЕрдВрджрд╛рдЬ рдХрд╛рдврддреЛ, рдЖрдгрд┐ рдордЧ metric рд╢реА рдХрд╕рд╛ рдЦреЗрд│ рд╣реЛрддреЛ рд╣реЗ рджрд╛рдЦрд╡рдгреНрдпрд╛рд╕рд╛рдареА рдкреНрд░рддреНрдпреЗрдХ deploy рддреАрди рд╡реЗрд│рд╛ рдореЛрдЬрддреЛ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 idp/demo.py measure
python3 - <<'EOF'
import sys; sys.path.insert(0, "idp"); from guard import dora
week = [(0, 30, False, None), (24, 40, True, 46), (48, 60, False, None), (72, 80, False, None), (96, 130, True, 131)]
print("one week:", dora(week, 1))
big = [(0, 160, True, 190)]
print("one big batch:", dora(big, 1))
print("re-runs counted:", dora(week * 2, 1)["per_week"], "deploys/week ┬╖ lead", dora(week * 2, 1)["lead_h"], "h")
EOF

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

measure рд╣реЗ рдЫрд╛рдкрддреЗ:

тФАтФА the four DORA keys for the science department, 4 weeks each (teaching data):
   before (tickets)     1.0 deploys/week ┬╖ lead time 150.0 h ┬╖ change failure rate 50% ┬╖ recovery 23.0 h
   after (paved path)   3.0 deploys/week ┬╖ lead time   5.0 h ┬╖ change failure rate  8% ┬╖ recovery 2.0 h
тФАтФА time to first deploy for a new team: 4 tickets in a row (repo, pipeline, database, DNS) ├Ч 2.0 days тЙИ 8.0 days
   on the golden path: scaffold + self-service database + one pipeline run of 15 min тАФ the same day
тФАтФА weaponize the metric ('deploy more!'): count every re-run of the deploy job тЖТ 9.0 deploys/week, lead time 5.0 h

рддреБрдордЪрд╛ snippet рд╣реЗ рдЫрд╛рдкрддреЛ:

one week: {'per_week': 5.0, 'lead_h': 16.0, 'cfr': 40, 'recover_h': 3.5}
one big batch: {'per_week': 1.0, 'lead_h': 160.0, 'cfr': 100, 'recover_h': 30.0}
re-runs counted: 10.0 deploys/week ┬╖ lead 16.0 h

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

Demo рдордзреНрдпреЗ рдЪрд╛рд░рд╣реА keys рдПрдХрддреНрд░ рд╣рд▓рддрд╛рдд: рд▓рд╣рд╛рди, рд╡рд╛рд░рдВрд╡рд╛рд░ рдмрджрд▓рд╛рдВрд╕реЛрдмрдд рдХрдореА failure rate рдЖрдгрд┐ рдЬрд▓рдж recovery рдЖрд▓реЗ тАФ DORA рдЪреЗ рд╕рдВрд╢реЛрдзрди рд╕рд╛рдВрдЧрддреЗ рддреЛ рдирдореБрдирд╛, рдирд┐рдпрдо рдирд╛рд╣реА. рдПрдХ рдореЛрдард╛ batch рдПрдХрд╛рдЪ рд╡реЗрд│реА рдЪрд╛рд░рд╣реА рдмрд╛рдмрддреАрдд рд╕рд░реНрд╡рд╛рдд рд╡рд╛рдИрдЯ рдард░рддреЛ. Re-runs рдореЛрдЬрд▓реНрдпрд╛рдиреЗ deployment frequency рджреБрдкреНрдкрдЯ рдХрд┐рдВрд╡рд╛ рддрд┐рдкреНрдкрдЯ рдЭрд╛рд▓реА, рддрд░ lead time рдЕрдЧрджреА рддрд╕рд╛рдЪ рд░рд╛рд╣рд┐рд▓рд╛ тАФ рдХреЛрдгреАрддрд░реА рдЖрдХрдбреНрдпрд╛рд╢реА рдЦреЗрд│рдд рдЕрд╕рд▓реНрдпрд╛рдЪреА рдЦреВрдг. рдкрд╣рд┐рд▓реНрдпрд╛ deploy рдкрд░реНрдпрдВрддрдЪрд╛ рд╡реЗрд│ tickets рдЪреНрдпрд╛ рд╕реБрдорд╛рд░реЗ 8 рджрд┐рд╡рд╕рд╛рдВрд╡рд░реВрди (рдзрдбрд╛ 01 рдЪреА рд╕рд░рд╛рд╕рд░реА рд╡рд╛рдЯ ├Ч 4 рд╡рд┐рдирдВрддреНрдпрд╛) рддреНрдпрд╛рдЪ рджрд┐рд╡рд╕рд╛рд╡рд░ рдЖрд▓рд╛, рдХрд╛рд░рдг рд╕реЛрдиреЗрд░реА рд╡рд╛рдЯ (03), self-service (05) рдЖрдгрд┐ paved pipeline (07) рдпрд╛рдВрдиреА рддреЗ рдЪрд╛рд░ tickets рдмрджрд▓рд▓реЗ.

ЁЯУП рд╢рд┐рдХрд╡рдгреНрдпрд╛рд╕рд╛рдареАрдЪрд╛ рдирдореБрдирд╛: рдЖрдзреА/рдирдВрддрд░рдЪрд╛ data рд╕реНрдкрд╖реНрдЯрддреЗрд╕рд╛рдареА рдмрдирд╡рд▓реЗрд▓рд╛ рдЖрд╣реЗ, рдЖрдгрд┐ 4 рдЖрдард╡рдбреЗ рд╣рд╛ рд▓рд╣рд╛рди рдирдореБрдирд╛ рдЖрд╣реЗ. Tickets рдЪрд╛ рдЕрдВрджрд╛рдЬ рдЕрд╕реЗ рдЧреГрд╣реАрдд рдзрд░рддреЛ рдХреА 4 рд╡рд┐рдирдВрддреНрдпрд╛ рдПрдХрд╛рдорд╛рдЧреВрди рдПрдХ рдХреЗрд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд. рдЦрд▒реНрдпрд╛ рдореЛрдЬрдорд╛рдкрд╛рдВрдирд╛ рдЬрд╛рд╕реНрдд рдореЛрдард╛ рдХрд╛рд▓рд╛рд╡рдзреА рд▓рд╛рдЧрддреЛ рдЖрдгрд┐ "failure" рдореНрд╣рдгреВрди рдХрд╛рдп рдореЛрдЬрд╛рдпрдЪреЗ рдпрд╛рдЪреА рдХрд╛рд│рдЬреА рдШреНрдпрд╛рд╡реА рд▓рд╛рдЧрддреЗ.

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

рдЪрд╛рд░ keys рдкреИрдХреА рдмрд╣реБрддреЗрдХ рддреБрдордЪреНрдпрд╛рдХрдбреЗ рдЖрдзреАрдЪ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ data рд╡рд░реВрди рдореЛрдЬрддрд╛ рдпреЗрддрд╛рдд. рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, deployment frequency рдЖрдгрд┐ lead time Git рдЖрдгрд┐ рддреБрдордЪреНрдпрд╛ deploy records рд╡рд░реВрди тАФ рдЦрд▒реНрдпрд╛ repo рд╡рд░ GitHub CLI рдиреЗ (deployments рддрд┐рдереЗ рдиреЛрдВрджрд▓реЗрд▓реЗ рдЕрд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗрдд, рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде рддреБрдордЪреНрдпрд╛ pipeline рдиреЗ):

gh api "repos/school/exam-results/deployments?environment=production&per_page=100" \
  --jq '.[] | [.sha, .created_at] | @tsv' > deploys.tsv
while IFS=$'\t' read -r sha deployed; do
  committed=$(git show -s --format=%cI "$sha")
  echo "$sha committed $committed deployed $deployed"
done < deploys.tsv

рд▓реЛрдХрд╛рдВрдЪреНрдпрд╛ рдмрд╛рдЬреВрд╕рд╛рдареА, рд▓рд╛рдВрдм рд╡рд╛рд░реНрд╖рд┐рдХ survey рдкреЗрдХреНрд╖рд╛ рд▓рд╣рд╛рди, рдирд┐рдпрдорд┐рдд survey рдЬрд╛рд╕реНрдд рдЪрд╛рдВрдЧрд▓рд╛ рдЪрд╛рд▓рддреЛ тАФ рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде 1тАУ5 рдорд╛рдкрд╛рд╡рд░ рддреАрди рдкреНрд░рд╢реНрди: "How easy was it to ship a change this month?", "How often were you blocked waiting for another team?", "How confident are you when you deploy?" тАФ рдЖрдгрд┐ рдПрдХ рдореЛрдХрд│реНрдпрд╛ рдЙрддреНрддрд░рд╛рдЪрд╛ рдкреНрд░рд╢реНрди. DevEx framework (Noda, Storey, Forsgren рдЖрдгрд┐ Greiler, 2023) feedback loops, cognitive load рдЖрдгрд┐ flow рднреЛрд╡рддреА survey рдкреНрд░рд╢реНрди рд╕реБрдЪрд╡рддреЛ.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдкреНрд░рддреНрдпреЗрдХ team рд▓рд╛ рддрд┐рдЪрд╛ рд╕реНрд╡рддрдГрдЪрд╛ рдХрд▓ рджрд╛рдЦрд╡рд╛, рдХреНрд░рдорд╡рд╛рд░реА рдирд╛рд╣реА. Platform рдЪрд╛ рдкрд░рд┐рдгрд╛рдо "paved path рд╡рд░рдЪреНрдпрд╛ teams рд╡рд┐рд░реБрджреНрдз рдЖрдзреА" рдЕрд╕рд╛ рд╕рд╛рдВрдЧрд╛, рдЖрдгрд┐ рдиреЗрд╣рдореА developers рдХрд╛рдп рдореНрд╣рдгрддрд╛рдд рддреНрдпрд╛рдЪреНрдпрд╛ рд╢реЗрдЬрд╛рд░реА.

тПня╕П рдкреБрдвреЗ

рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдЪреЗ рдЖрддрд╛ рдЕрдиреЗрдХ рднрд╛рдЧ рдЖрд╣реЗрдд. рд╢реЗрд╡рдЯрдЪрд╛ рдзрдбрд╛ рддреНрдпрд╛рдВрдирд╛ рдПрдХрд╛ рд╕реНрд╡рд╛рдЧрдд рдХрдХреНрд╖рд╛ рдорд╛рдЧреЗ рдЖрдгрддреЛ: developer portal тАФ рдЖрдгрд┐ рд╕рдВрдкреВрд░реНрдг рдЪрд┐рддреНрд░ рдХрд╛рдврддреЛ.

git checkout lesson-12-developer-portal

ЁЯУК Lesson 11 тАФ Measuring the platform: is the office helping?

ЁЯУН You are here: Lesson 11 of 12 ┬╖ Previous: lesson-10-scorecards ┬╖ Next: lesson-12-developer-portal


ЁЯУж What's in this branch

Lessons 01тАУ10, plus how to measure whether the platform helps. The four DORA keys computed from a list of deployments, time to first deploy for a new team, the SPACE framework for the parts numbers miss тАФ and a demonstration of how a metric turned into a target stops meaning anything. dora() in idp/guard.py, measure() in idp/demo.py.

ЁЯзТ Explain like I'm 5

The head teacher asks Dipika: "You built kits, a catalogue and a key cabinet. Did it help?"

Dipika looks at the science department's changes to its classrooms, four weeks before the kits and four weeks after:

Then the head teacher says: "Great! From now on every department must make 9 changes a week." ЁЯУИ The next week the maths department reports 9... by counting the same change three times. The number went up. Nothing got better for the students.

So Dipika also asks the teachers: "Is your work easier? Do you get stuck less?" The numbers and the answers together tell the truth. Numbers alone can be bent.

ЁЯЧ║я╕П Diagram

flowchart LR
    d["ЁЯУж deployments<br/>commit hour ┬╖ deploy hour ┬╖<br/>failed? ┬╖ restored hour"] --> k["ЁЯФС four DORA keys"]
    k --> f["deploys/week<br/>1.0 тЖТ 3.0"]
    k --> lt["lead time<br/>150 h тЖТ 5 h"]
    k --> cfr["change failure rate<br/>50% тЖТ 8%"]
    k --> rt["recovery time<br/>23 h тЖТ 2 h"]
    g["тЪая╕П count re-runs:<br/>9.0/week, nothing better"] -.-> f
    sp["ЁЯЧгя╕П SPACE: ask people too"] --> k

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/platform-engineering/lesson-diagrams.html#l11

тЭУ What

ЁЯдФ Why

Because a platform costs a team of people, and someone will ask what it bought. "It feels faster" does not survive a budget meeting; four numbers from real deployments, compared before and after, often do. But the same numbers, once they become targets, change the behaviour they were meant to observe. So measure the platform's effect on teams, share the numbers with the teams, and pair them with asking people.

ЁЯФз How (in this repo)

dora(deploys, weeks) in idp/guard.py takes deployments as (commit_hour, deploy_hour, failed, restored_hour). It returns deployments per week, the median lead time in hours, the change failure rate as a whole percent, and the median recovery time of the failed ones. (DORA's surveys ask for ranges; medians are one reasonable way to summarise your own data.) measure() in idp/demo.py compares science's 4 weeks before (BEFORE, 4 deploys) and after (AFTER, 12 deploys), estimates time to first deploy from lesson 01's queue, and then counts each deploy three times to show the metric being gamed.

ЁЯзк Try it

python3 idp/demo.py measure
python3 - <<'EOF'
import sys; sys.path.insert(0, "idp"); from guard import dora
week = [(0, 30, False, None), (24, 40, True, 46), (48, 60, False, None), (72, 80, False, None), (96, 130, True, 131)]
print("one week:", dora(week, 1))
big = [(0, 160, True, 190)]
print("one big batch:", dora(big, 1))
print("re-runs counted:", dora(week * 2, 1)["per_week"], "deploys/week ┬╖ lead", dora(week * 2, 1)["lead_h"], "h")
EOF

тЬЕ Verify тАФ what you should see

measure prints:

тФАтФА the four DORA keys for the science department, 4 weeks each (teaching data):
   before (tickets)     1.0 deploys/week ┬╖ lead time 150.0 h ┬╖ change failure rate 50% ┬╖ recovery 23.0 h
   after (paved path)   3.0 deploys/week ┬╖ lead time   5.0 h ┬╖ change failure rate  8% ┬╖ recovery 2.0 h
тФАтФА time to first deploy for a new team: 4 tickets in a row (repo, pipeline, database, DNS) ├Ч 2.0 days тЙИ 8.0 days
   on the golden path: scaffold + self-service database + one pipeline run of 15 min тАФ the same day
тФАтФА weaponize the metric ('deploy more!'): count every re-run of the deploy job тЖТ 9.0 deploys/week, lead time 5.0 h

Your snippet prints:

one week: {'per_week': 5.0, 'lead_h': 16.0, 'cfr': 40, 'recover_h': 3.5}
one big batch: {'per_week': 1.0, 'lead_h': 160.0, 'cfr': 100, 'recover_h': 30.0}
re-runs counted: 10.0 deploys/week ┬╖ lead 16.0 h

ЁЯПБ What you just proved

The four keys move together in the demo: smaller, more frequent changes came with a lower failure rate and faster recovery тАФ the pattern DORA's research reports, not a law. One big batch is the worst of all four at once. Counting re-runs doubled or tripled deployment frequency while lead time stayed exactly the same тАФ a sign someone is playing the number. Time to first deploy fell from about 8 days of tickets (lesson 01's average wait ├Ч 4 requests) to the same day, because the golden path (03), self-service (05) and the paved pipeline (07) replaced the four tickets.

ЁЯУП Teaching model: the before/after data is invented to be clear, and 4 weeks is a small sample. The ticket estimate assumes the 4 requests are made one after another. Real measurements need longer periods and care about what counts as a "failure".

тЪая╕П Common mistakes

ЁЯПн In production

Most of the four keys can be computed from data you already have. For example, deployment frequency and lead time from Git and your deploy records тАФ on a real repo with the GitHub CLI (deployments must be recorded there, for example by your pipeline):

gh api "repos/school/exam-results/deployments?environment=production&per_page=100" \
  --jq '.[] | [.sha, .created_at] | @tsv' > deploys.tsv
while IFS=$'\t' read -r sha deployed; do
  committed=$(git show -s --format=%cI "$sha")
  echo "$sha committed $committed deployed $deployed"
done < deploys.tsv

For the people side, a short, regular survey works better than a long yearly one тАФ for example three questions on a 1тАУ5 scale: "How easy was it to ship a change this month?", "How often were you blocked waiting for another team?", "How confident are you when you deploy?" тАФ plus one free-text question. The DevEx framework (Noda, Storey, Forsgren and Greiler, 2023) suggests survey questions around feedback loops, cognitive load and flow.

ЁЯПн Why this matters in production: show each team its own trend, not a ranking. Report the platform's effect as "teams on the paved path vs before", and always next to what developers say.

тПня╕П Next

The office now has many parts. The last lesson puts them behind one front desk: the developer portal тАФ and draws the whole picture.

git checkout lesson-12-developer-portal
тЖР PreviousscorecardsNext тЖТdeveloper portal

This page is the lesson's README from the lesson-11-measuring branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.