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

ЁЯзн рдзрдбрд╛ 01 тАФ platforms рдХрд╛: рдкреНрд░рддреНрдпреЗрдХ рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рд╕реНрд╡рддрдГрдЪрд╛ рд╡рд░реНрдЧ-рд╕рдВрдЪ рдмрдирд╡рддреЗ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 01 ┬╖ рдкреБрдвреЗ: lesson-02-platform-as-product


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

рд╣рд╛ рд╕рдВрдкреВрд░реНрдг рдХреЛрд░реНрд╕ рдЬреНрдпрд╛ рд╕рдорд╕реНрдпреЗрдмрджреНрджрд▓ рдЖрд╣реЗ рддреА. рд╢рд╛рд│рд╛ рд╡рд╛рдврддреЗ рдЖрд╣реЗ. рдкреНрд░рддреНрдпреЗрдХ рдирд╡реНрдпрд╛ рд╢рд┐рдХреНрд╖рд┐рдХреЗрд▓рд╛ рдПрдХ рд╡рд░реНрдЧрдЦреЛрд▓реА рд▓рд╛рдЧрддреЗ: рдмрд╛рдХреЗ, projector, рдХреБрд▓реВрдк, рд╡реЗрд│рд╛рдкрддреНрд░рдХрд╛рддрд▓рд╛ рддрд╛рд╕, рдкреНрд░рдердореЛрдкрдЪрд╛рд░ рдкреЗрдЯреА. рдЖрдЬ рдкреНрд░рддреНрдпреЗрдХ рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рдЖрдкрд▓рд╛ рд╕рдВрдЪ рд╕реНрд╡рддрдГ рдмрдирд╡рддреЗ, рдХрд┐рдВрд╡рд╛ рд╕реБрд╡рд┐рдзрд╛ рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рд▓рд╛ рд╡рд┐рдирдВрддреА рд▓рд┐рд╣реВрди рд░рд╛рдВрдЧреЗрдд рд╡рд╛рдЯ рдкрд╛рд╣рддреЗ. рд╣рд╛ рдХреЛрд░реНрд╕ рддреНрдпрд╛ рджрд┐рд╡рд╕рд╛рдмрджреНрджрд▓ рдЖрд╣реЗ рдЬреЗрд╡реНрд╣рд╛ рд╕реБрд╡рд┐рдзрд╛ рдХрд╛рд░реНрдпрд╛рд▓рдп рд░рд╛рдВрдЧ рдЕрд╕рд▓реЗрд▓рд╛ counter рди рд░рд╛рд╣рддрд╛ рдПрдХ service рдмрдирддреЗ: рддрдпрд╛рд░ рд╕рдВрдЪ, рдПрдХ catalogue рдЖрдгрд┐ рдПрдХ рдмрд╛рдВрдзрд▓реЗрд▓реА рд╡рд╛рдЯ. software рдЪреНрдпрд╛ рднрд╛рд╖реЗрдд: internal developer platform (IDP) рдмрдирд╡рдгрд╛рд░реА рдПрдХ platform team. рд╕рдВрдкреВрд░реНрдг рдХреЛрд░реНрд╕рднрд░ рддреБрдореНрд╣реА рд╡рд╛рдкрд░рд╛рд▓ рдЕрд╢рд╛ рдЦрд▒реНрдпрд╛ files:

ЁЯОТ рд╕реБрд░реВ рдХрд░рдгреНрдпрд╛рдЖрдзреА: рддреБрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд Python 3 рд▓рд╛рдЧрддреЗ, рджреБрд╕рд░реЗ рдХрд╛рд╣реА рдирд╛рд╣реА тАФ cloud account рдирд╛рд╣реА, pip install рдирд╛рд╣реА. Package рдЪреЗ рдирд╛рд╡ idp рдЖрд╣реЗ, platform рдирд╛рд╣реА, рдХрд╛рд░рдг platform рд╣рд╛ Python standard-library рдЪрд╛ module рдЖрд╣реЗ рдЖрдгрд┐ рддреНрдпрд╛ рдирд╛рд╡рд╛рдЪреЗ folder рддреЛ рд▓рдкрд╡реВрди рдЯрд╛рдХреЗрд▓. idp/ рдордзрд▓реЗ рд╕рдЧрд│реЗ рдПрдХ рд╢рд┐рдХрд╡рдгреНрдпрд╛рдЪреЗ рдореЙрдбреЗрд▓ рдЖрд╣реЗ: рддреЗ рдореЛрдЬрддреЗ, рддрдкрд╛рд╕рддреЗ рдЖрдгрд┐ рдирд╛рд╡реЗ рджреЗрддреЗ, рдкрдг рдЦрд░реЗ рдХрд╛рд╣реАрд╣реА рдмрдирд╡рдд рдирд╛рд╣реА. рдЦрд▒реНрдпрд╛ account рд╡рд░ рдЕрд╕реЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ commands рдирд╛ рдЦрд░реА tools (Backstage, Terraform, kubectl тАж) рд▓рд╛рдЧрддрд╛рдд рдЖрдгрд┐ рддреНрдпрд╛рдВрдирд╛ рдкреИрд╕реЗ рд▓рд╛рдЧреВ рд╢рдХрддрд╛рдд.

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

рдХрддрд░рд┐рдирд╛ рд╣реА рд╡рд┐рдЬреНрдЮрд╛рдирд╛рдЪреА рдирд╡реА рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рдЖрд╣реЗ. рдкрд╣рд┐рд▓реНрдпрд╛ рджрд┐рд╡рд╢реА рддрд┐рд▓рд╛ рдПрдХ рд╡рд░реНрдЧрдЦреЛрд▓реА рд╣рд╡реА рдЖрд╣реЗ.

рддрд┐рд▓рд╛ рдХреЛрдгреАрдЪ рддреА рджреЗрдд рдирд╛рд╣реА. рддрд┐рд▓рд╛ рдмрд╛рдХреЗ рд╢реЛрдзрд╛рд╡реА рд▓рд╛рдЧрддрд╛рдд. ЁЯкС projector рдЪреНрдпрд╛ socket рд╕рд╛рдареА electrician рд▓рд╛ рд╡рд┐рдЪрд╛рд░рд╛рд╡реЗ рд▓рд╛рдЧрддреЗ. рдХрд┐рд▓реНрд▓реАрд╕рд╛рдареА рдХреБрд▓реВрдкрд╡рд╛рд▓реНрдпрд╛рд▓рд╛ рд╡рд┐рдЪрд╛рд░рд╛рд╡реЗ рд▓рд╛рдЧрддреЗ. ЁЯФС рддрд╛рд╕рд╛рд╕рд╛рдареА рд╡реЗрд│рд╛рдкрддреНрд░рдХ рд▓рд┐рдкрд┐рдХрд╛рд▓рд╛ рд╡рд┐рдЪрд╛рд░рд╛рд╡реЗ рд▓рд╛рдЧрддреЗ. рдкреНрд░рдердореЛрдкрдЪрд╛рд░ рдкреЗрдЯреАрд╕рд╛рдареА рдкрд░рд┐рдЪрд╛рд░рд┐рдХреЗрд▓рд╛ рд╡рд┐рдЪрд╛рд░рд╛рд╡реЗ рд▓рд╛рдЧрддреЗ. рдкреНрд░рддреНрдпреЗрдХрд╛рдХрдбреЗ рдПрдХ рдлреЙрд░реНрдо рдЖрд╣реЗ. рдкреНрд░рддреНрдпреЗрдХ рдлреЙрд░реНрдо рдПрдХрд╛ рдврд┐рдЧрд╛рдд рдЬрд╛рддреЛ.

рдРрд╢реНрд╡рд░реНрдпрд╛, рдЧрдгрд┐рдд рд╡рд┐рднрд╛рдЧрд╛рдЪреА рдкреНрд░рдореБрдЦ, рд╣реЗ рд╕рдЧрд│реЗ рджрд╣рд╛ рд╡рд░реНрд╖рд╛рдВрдкреВрд░реНрд╡реА рдХрд░реВрди рдмрд╕рд▓реА рд╣реЛрддреА. рддрд┐рд▓рд╛ рддреНрдпрд╛рддрд▓реЗ рдмрд░реЗрдЪрд╕реЗ рдЖрдард╡рддреЗ. рдкрдг рддреА рдкреНрд░рддреНрдпреЗрдХ рд╢реБрдХреНрд░рд╡рд╛рд░реА рд╢рд┐рдХрд╡рдгреНрдпрд╛рдРрд╡рдЬреА рдЖрдкрд▓рд╛ projector рджреБрд░реБрд╕реНрдд рдХрд░рдгреНрдпрд╛рдд рдШрд╛рд▓рд╡рддреЗ.

рд╕реБрд╡рд┐рдзрд╛ рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдд рдПрдХрдЪ рд▓рд┐рдкрд┐рдХ рдЖрд╣реЗ, рджреАрдкрд┐рдХрд╛. рд░реЛрдЬ рдкрд╛рдЪ рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рддрд┐рд▓рд╛ рд▓рд┐рд╣рд┐рддрд╛рдд. рддреА рд░реЛрдЬ рдЪрд╛рд░ рд╡рд┐рдирдВрддреНрдпрд╛ рдкреВрд░реНрдг рдХрд░реВ рд╢рдХрддреЗ. рдореНрд╣рдгреВрди рд░реЛрдЬ рдПрдХ рдлреЙрд░реНрдо рдврд┐рдЧрд╛рдд рдЬрд╛рд╕реНрдд рд╡рд╛рдЯ рдкрд╛рд╣рддреЛ. рдПрдХрд╛ рдорд╣рд┐рдиреНрдпрд╛рдирдВрддрд░, рдирд╡реНрдпрд╛ рд╢рд┐рдХреНрд╖рд┐рдХреЗрд▓рд╛ рд╕рд╛рдзреНрдпрд╛ рдХрд┐рд▓реНрд▓реАрд╕рд╛рдареА рджрд┐рд╡рд╕реЗрдВрджрд┐рд╡рд╕ рдерд╛рдВрдмрд╛рд╡реЗ рд▓рд╛рдЧрддреЗ.

рдордЧ рд╢рд╛рд│реЗрд▓рд╛ рдПрдХ рдХрд▓реНрдкрдирд╛ рд╕реБрдЪрддреЗ. рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдХрдбреЗ рддрдпрд╛рд░ рд╡рд░реНрдЧ-рд╕рдВрдЪ рдЕрд╕рд▓реЗ рддрд░? ЁЯУж рдмрд╛рдХреЗ, socket, рдХреБрд▓реВрдк рдЖрдгрд┐ рдкреНрд░рдердореЛрдкрдЪрд╛рд░ рдкреЗрдЯреА, рд╕рдЧрд│реЗ рдПрдХрддреНрд░, рддрдкрд╛рд╕рд▓реЗрд▓реЗ, рд╕рдЧрд│реНрдпрд╛рдВрд╕рд╛рдареА рд╕рд╛рд░рдЦреЗ. рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рдлрд│реАрд╡рд░реВрди рдПрдХ рд╕рдВрдЪ рдЙрдЪрд▓рддреЗ. рддреА рдЕрдЬреВрдирд╣реА рдЖрдкрд▓реЗ рдзрдбреЗ рд╕реНрд╡рддрдГ рд╢рд┐рдХрд╡рддреЗ, рдЖрдгрд┐ рдЕрдЬреВрдирд╣реА рдЖрдкрд▓реА рдЦреЛрд▓реА рд╕реНрд╡рддрдГ рд╕рд╛рдВрднрд╛рд│рддреЗ. рдлрдХреНрдд рддрд┐рд▓рд╛ рдЖрдзреА рдЦреЛрд▓реА рдмрд╛рдВрдзрд╛рд╡реА рд▓рд╛рдЧрдд рдирд╛рд╣реА.

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

flowchart LR
    subgraph before["ЁЯШй before: every teacher alone"]
      t1["ЁЯСйтАНЁЯПл Katrina<br/>14 concerns to handle"] --> q["ЁЯУе the office's ticket pile<br/>5 in, 4 out, every day"]
      q --> w["тП│ wait: 0 days on day 1,<br/>4 days on day 20"]
    end
    subgraph after["ЁЯЫдя╕П after: the facilities office"]
      t2["ЁЯСйтАНЁЯПл Katrina<br/>keeps 4: code, tests,<br/>alerts, on-call"] --> k["ЁЯУж 10 ready kits<br/>Dockerfile, CI, Terraform,<br/>IAM, DNS + TLS тАж"]
      k --> s["ЁЯПл a classroom (service)<br/>the same day"]
    end

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рд╡рд╛рдврдгрд╛рд░реА рд╢рд╛рд│рд╛ рджреЛрдиреНрд╣реА рднрд┐рдВрддреАрдВрд╡рд░ рдПрдХрд╛рдЪ рд╡реЗрд│реА рдЖрджрд│рддреЗ. рдкреНрд░рддреНрдпреЗрдХ рд╢рд┐рдХреНрд╖рд┐рдХреЗрдиреЗ рдЖрдкрд▓реА рдЦреЛрд▓реА рд╕реНрд╡рддрдГ рдмрд╛рдВрдзрд▓реА рддрд░ рдкреНрд░рддреНрдпреЗрдХреАрд▓рд╛ рд╡реЗрд│ рд▓рд╛рдЧрддреЛ, рдЖрдгрд┐ рд╕рдЧрд│реНрдпрд╛ рдЦреЛрд▓реНрдпрд╛ рд╡реЗрдЧрд│реНрдпрд╛ рдЕрд╕рддрд╛рдд тАФ рддреНрдпрд╛рдореБрд│реЗ рдХреЛрдгреАрдЪ рдХреЛрдгрд╛рд▓рд╛ рдорджрдд рдХрд░реВ рд╢рдХрдд рдирд╛рд╣реА. рдПрдХрд╛рдЪ рд▓рд┐рдкрд┐рдХрд╛рдиреЗ рдкреНрд░рддреНрдпреЗрдХ рдЦреЛрд▓реА рд╣рд╛рддрд╛рдиреЗ рдмрд╛рдВрдзрд▓реА рддрд░ рддреЛ рд▓рд┐рдкрд┐рдХрдЪ bottleneck рдмрдирддреЛ. Platform рд╣рд╛ рддрд┐рд╕рд░рд╛ рдорд╛рд░реНрдЧ рдЖрд╣реЗ: рдХрд╛рд░реНрдпрд╛рд▓рдп рд╕рдВрдЪ рдПрдХрджрд╛рдЪ, рдЪрд╛рдВрдЧрд▓реЗ рдмрдирд╡рддреЗ, рдЖрдгрд┐ рд╢рд┐рдХреНрд╖рд┐рдХрд╛ рд╕реНрд╡рддрдГрдЪреА рдорджрдд рд╕реНрд╡рддрдГ рдХрд░рддрд╛рдд. Service рдЪреА рдорд╛рд▓рдХреА рдЕрдЬреВрдирд╣реА team рдХрдбреЗрдЪ рдЕрд╕рддреЗ ("you build it, you run it" рдХрд╛рдпрдо рд░рд╛рд╣рддреЗ) тАФ рдлрдХреНрдд рддреА рдЬреНрдпрд╛ рдЬрдорд┐рдиреАрд╡рд░ рдЙрднреА рдЖрд╣реЗ рддреА рдкреБрдиреНрд╣рд╛ рдкреБрдиреНрд╣рд╛ рд╢реЛрдзреВрди рдХрд╛рдврдгреЗ рдерд╛рдВрдмрд╡рддреЗ.

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

idp/office.py рдордзрд▓реЗ cognitive_load(concerns, kits) рдПрдХ service рдЪрд╛рд▓реВ рдХрд░рдгреНрдпрд╛рдЪреНрдпрд╛ 14 CONCERNS рдЪреА рд╡рд┐рднрд╛рдЧрдгреА рдХрд░рддреЗ: team рдХрд╛рдп рдареЗрд╡рддреЗ рдЖрдгрд┐ OFFICE_KITS рдХрд╛рдп рд╕рд╛рдВрднрд╛рд│рддреЗ. ticket_queue(arrivals_per_day, capacity_per_day) рд╣реА рдЖрдзреА рдЖрд▓реЗрд▓реЗ рдЖрдзреА рдЕрд╢реА рд░рд╛рдВрдЧ рдЖрд╣реЗ: рд░реЛрдЬ рдирд╡реАрди tickets рдпреЗрддрд╛рдд, рдХрд╛рд░реНрдпрд╛рд▓рдп рддреНрдпрд╛рддрд▓реА рдЬрд╛рд╕реНрддреАрдд рдЬрд╛рд╕реНрдд capacity рдкреВрд░реНрдг рдХрд░рддреЗ, рдЖрдгрд┐ рддреЗ рдкреНрд░рддреНрдпреЗрдХ рдкреВрд░реНрдг рдЭрд╛рд▓реЗрд▓реНрдпрд╛ ticket рдЪреА рд╡рд╛рдЯ (рджрд┐рд╡рд╕рд╛рдВрдд) рдЖрдгрд┐ рдЕрдЬреВрди рдХрд┐рддреА рд╡рд╛рдЯ рдкрд╛рд╣рдд рдЖрд╣реЗрдд рддреЗ рдкрд░рдд рджреЗрддреЗ. idp/demo.py рдордзрд▓реЗ why() рд░реЛрдЬ 5 рд╡рд┐рдирдВрддреНрдпрд╛рдВрдЪреЗ 20 рдХрд╛рдорд╛рдЪреЗ рджрд┐рд╡рд╕, 4 рдЪреНрдпрд╛ capacity рдиреЗ рдЪрд╛рд▓рд╡рддреЗ.

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

python3 idp/demo.py why
python3 - <<'EOF'
import sys; sys.path.insert(0, "idp"); from office import ticket_queue, cognitive_load, CONCERNS, OFFICE_KITS
for cap in (4, 5, 6):
    waits, left = ticket_queue([5] * 20, cap)
    print(f"office does {cap}/day тЖТ average wait {sum(waits) / len(waits):.1f} days ┬╖ longest {max(waits)} ┬╖ still waiting {left}")
waits, left = ticket_queue([5] * 10 + [8] * 5 + [5] * 5, 5)
print(f"a busy week (8/day) with capacity 5 тЖТ longest wait {max(waits)} days ┬╖ still waiting {left} at the end")
team, kits = cognitive_load(CONCERNS, OFFICE_KITS - {"backups"})
print(f"no backups kit тЖТ the team keeps {len(team)}: {team}")
EOF
python3 idp/test_idp.py

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

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

тФАтФА to open one classroom (ship one service) a teacher's team must handle 14 concerns alone
   with the facilities office: the team keeps 4 (application code, tests, alerts, on-call for the service)
тФАтФА the ticket queue: 5 requests a day, the office finishes 4 a day, for 20 working days
   finished 80 ┬╖ still waiting 20 ┬╖ average wait 2.0 days
   a ticket finished on day 1 waited 0 days ┬╖ one finished on day 20 waited 4 days тАФ the line grows by 1 every day

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

office does 4/day тЖТ average wait 2.0 days ┬╖ longest 4 ┬╖ still waiting 20
office does 5/day тЖТ average wait 0.0 days ┬╖ longest 0 ┬╖ still waiting 0
office does 6/day тЖТ average wait 0.0 days ┬╖ longest 0 ┬╖ still waiting 0
a busy week (8/day) with capacity 5 тЖТ longest wait 3 days ┬╖ still waiting 15 at the end
no backups kit тЖТ the team keeps 5: ['application code', 'tests', 'alerts', 'backups', 'on-call for the service']

Tests рд╢реЗрд╡рдЯреА 12/12 passed рдЫрд╛рдкрддрд╛рдд.

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

Capacity 5 рдЕрд╕рддрд╛рдирд╛ рд░рд╛рдВрдЧ рдХрдзреАрдЪ рддрдпрд╛рд░ рд╣реЛрдд рдирд╛рд╣реА. Capacity 4 рдЕрд╕рддрд╛рдирд╛ тАФ рд░реЛрдЬ рдлрдХреНрдд рдПрдХ рд╡рд┐рдирдВрддреА рдХрдореА тАФ рд░рд╛рдВрдЧ рд░реЛрдЬ рдПрдХрдиреЗ рд╡рд╛рдврддреЗ рдЖрдгрд┐ рдХрдзреАрдЪ рд╕рд╛рд╡рд░рдд рдирд╛рд╣реА. рд░реЛрдЬ 8 рдЪрд╛ рдПрдХ рдЧрд░реНрджреАрдЪрд╛ рдЖрдард╡рдбрд╛ рдЧрд░реНрджреА рд╕рдВрдкрд▓реНрдпрд╛рдирдВрддрд░рд╣реА 15 tickets рд╡рд╛рдЯ рдкрд╛рд╣рдд рдареЗрд╡рддреЛ. рдЬрд╛рд╕реНрдд рд▓рд┐рдкрд┐рдХ рдиреЗрдорд▓реНрдпрд╛рдиреЗ рднрд┐рдВрдд рдкреБрдвреЗ рд╕рд░рдХрддреЗ; рддреА рдирд╛рд╣реАрд╢реА рд╣реЛрдд рдирд╛рд╣реА, рдХрд╛рд░рдг рд╢рд╛рд│реЗрдмрд░реЛрдмрд░ рд╡рд┐рдирдВрддреНрдпрд╛рд╣реА рд╡рд╛рдврддрд╛рдд. Self-service рд░рд╛рдВрдЧрдЪ рдХрд╛рдвреВрди рдЯрд╛рдХрддреЗ. рдЖрдгрд┐ рдХрд╛рд░реНрдпрд╛рд▓рдп рдЬреЛ рд╕рдВрдЪ рджреЗрдд рдирд╛рд╣реА рддреЛ рдкрд░рдд team рд╡рд░ рдпреЗрдКрди рдкрдбрддреЛ (рд╡рд░ backups).

ЁЯУП рд╢рд┐рдХрд╡рдгреНрдпрд╛рдЪреЗ рдореЙрдбреЗрд▓: рдпрд╛ рд░рд╛рдВрдЧреЗрдд рдпреЗрдгрд╛рд▒реНрдпрд╛ рд╡рд┐рдирдВрддреНрдпрд╛ рдЖрдгрд┐ capacity рдард░рд▓реЗрд▓реНрдпрд╛ рдЖрд╣реЗрдд. рдЦрд▒реНрдпрд╛ рд╡рд┐рдирдВрддреНрдпрд╛ рдЕрдЪрд╛рдирдХ рдЭреБрдВрдбреАрдиреЗ рдпреЗрддрд╛рдд, рддреНрдпрд╛рдореБрд│реЗ рд╡рд╛рдЯ рдпрд╛ рдореЙрдбреЗрд▓рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рд╡рд╛рдИрдЯ рд╣реЛрддреЗ, рдЪрд╛рдВрдЧрд▓реА рдирд╛рд╣реА.

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

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

рдХрд╛рд╣реАрд╣реА рдмрдирд╡рдгреНрдпрд╛рдЖрдзреА, рддреБрдордЪреНрдпрд╛рдХрдбреЗ рдЖрдзреАрдЪ рдЕрд╕рд▓реЗрд▓реА рд░рд╛рдВрдЧ рдореЛрдЬрд╛. рдЦрд▒реНрдпрд╛ account рд╡рд░, рдмрд╣реБрддреЗрдХ ticket systems рд╡рд┐рдирдВрддреНрдпрд╛ export рдХрд░реВ рд╢рдХрддрд╛рдд; рдкрд╣рд┐рд▓реНрдпрд╛ рдирдЬрд░реЗрд╕рд╛рдареА рдлрдХреНрдд рддреАрди columns рд▓рд╛рдЧрддрд╛рдд:

# tickets.csv: created,resolved,type   (from Jira, ServiceNow, GitHub issues тАж)
python3 -c "
import csv, datetime as d, statistics as s, collections as c
rows = list(csv.DictReader(open('tickets.csv')))
wait = c.defaultdict(list)
for r in rows:
    t = d.datetime.fromisoformat
    wait[r['type']].append((t(r['resolved']) - t(r['created'])).days)
for k, v in sorted(wait.items(), key=lambda kv: -len(kv[1])):
    print(f'{k:<20} {len(v):>4} tickets ┬╖ median wait {s.median(v)} days')
"

рддреНрдпрд╛ рдпрд╛рджреАрдд рд╕рд░реНрд╡рд╛рдд рд╡рд░ рдЕрд╕рд▓реЗрд▓реЗ рдкреНрд░рдХрд╛рд░ тАФ "new database", "new repo", "DNS record", "firewall change" тАФ рд╣реЗрдЪ рдЖрдзреА рдмрдирд╡рд╛рдпрдЪреЗ рд╕рдВрдЪ рдЖрд╣реЗрдд.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: platform рдЪреЗ рд╕рдорд░реНрдерди рддреЛ рдХрд╛рдвреВрди рдЯрд╛рдХрдд рдЕрд╕рд▓реЗрд▓реА рд╡рд╛рдЯ рдЖрдгрд┐ рдкреБрдирд░рд╛рд╡реГрддреНрддреАрдЪреЗ рдХрд╛рдо рдпрд╛рд╡рд░реВрди рд╣реЛрддреЗ. рд╕реБрд░реВ рдХрд░рдгреНрдпрд╛рдЖрдзреА рджреЛрдиреНрд╣реА рд▓рд┐рд╣реВрди рдареЗрд╡рд╛, рдореНрд╣рдгрдЬреЗ рдирдВрддрд░ рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдЪрд╛ рдЙрдкрдпреЛрдЧ рдЭрд╛рд▓рд╛ рдХрд╛ рд╣реЗ рддреБрдореНрд╣реА рджрд╛рдЦрд╡реВ рд╢рдХрд╛рд▓ (рдзрдбрд╛ 11).

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

рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛ рд╣реЗ рдорд╛рд╣реАрдд рдЖрд╣реЗ. рдкреБрдврдЪрд╛ рдкреНрд░рд╢реНрди рдЖрд╣реЗ рдЖрдзреА рдХрд╛рдп рдмрдирд╡рд╛рдпрдЪреЗ тАФ рдЖрдгрд┐ рддреНрдпрд╛рдЪреЗ рдЙрддреНрддрд░ рдХрд╛рд░реНрдпрд╛рд▓рдпрд╛рдХрдбреВрди рдирд╛рд╣реА, рддрд░ рд╢рд┐рдХреНрд╖рд┐рдХрд╛рдВрдХрдбреВрди рдпреЗрддреЗ.

git checkout lesson-02-platform-as-product

ЁЯзн Lesson 01 тАФ Why platforms: every teacher building her own classroom kit

ЁЯУН You are here: Lesson 01 of 12 ┬╖ Next: lesson-02-platform-as-product


ЁЯУж What's in this branch

The problem this whole course is about. The school is growing. Every new teacher needs a classroom: desks, a projector, a lock, a timetable slot, a first-aid box. Today every teacher builds her own kit, or writes a request to the facilities office and waits in line. This course is about the day the facilities office stops being a counter with a queue and becomes a service: ready kits, a catalogue and a paved path. In software words: a platform team building an internal developer platform (IDP). Real files you will use all the way through:

ЁЯОТ Before you start: you need Python 3 and nothing else тАФ no cloud account, no pip install. The package is called idp, not platform, because platform is a Python standard-library module and a folder with that name would hide it. Everything in idp/ is a teaching model: it counts, checks and names things, and creates nothing real. Commands marked on a real account need real tools (Backstage, Terraform, kubectl тАж) and can cost money.

ЁЯзТ Explain like I'm 5

Katrina is a new science teacher. On her first day she needs a classroom.

Nobody gives her one. She has to find desks. ЁЯкС Ask the electrician for a projector socket. Ask the locksmith for a key. ЁЯФС Ask the timetable clerk for a slot. Ask the nurse for a first-aid box. Each person has a form. Each form goes into a pile.

Aishwarya, the head of maths, did all this ten years ago. She remembers most of it. But she spends every Friday fixing her own projector instead of teaching.

The facilities office has one clerk, Dipika. Five teachers write to her each day. She can finish four requests a day. So every day one more form waits in the pile. After a month, a new teacher waits days for a simple key.

Then the school has an idea. What if the office had ready classroom kits? ЁЯУж Desks, socket, lock and first-aid box, all in one, tested, the same for everybody. A teacher takes one from the shelf. She still teaches her own lessons, and she still looks after her own room. She just does not have to build the room first.

ЁЯЧ║я╕П Diagram

flowchart LR
    subgraph before["ЁЯШй before: every teacher alone"]
      t1["ЁЯСйтАНЁЯПл Katrina<br/>14 concerns to handle"] --> q["ЁЯУе the office's ticket pile<br/>5 in, 4 out, every day"]
      q --> w["тП│ wait: 0 days on day 1,<br/>4 days on day 20"]
    end
    subgraph after["ЁЯЫдя╕П after: the facilities office"]
      t2["ЁЯСйтАНЁЯПл Katrina<br/>keeps 4: code, tests,<br/>alerts, on-call"] --> k["ЁЯУж 10 ready kits<br/>Dockerfile, CI, Terraform,<br/>IAM, DNS + TLS тАж"]
      k --> s["ЁЯПл a classroom (service)<br/>the same day"]
    end

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

тЭУ What

ЁЯдФ Why

Because a growing school hits both walls at once. If every teacher builds her own room, each one is slow, and all the rooms are different тАФ so nobody can help anybody. If one clerk builds every room by hand, the clerk becomes the bottleneck. A platform is the third way: the office builds the kits once, well, and teachers help themselves. The team still owns its service ("you build it, you run it" stays) тАФ it just stops re-inventing the ground it stands on.

ЁЯФз How (in this repo)

cognitive_load(concerns, kits) in idp/office.py splits the 14 CONCERNS of shipping one service into what the team keeps and what OFFICE_KITS covers. ticket_queue(arrivals_per_day, capacity_per_day) is a first-come, first-served line: each day new tickets join, the office finishes up to capacity of them, and it returns the wait (in days) of every finished ticket plus how many are still waiting. why() in idp/demo.py runs 20 working days of 5 requests a day with a capacity of 4.

ЁЯзк Try it

python3 idp/demo.py why
python3 - <<'EOF'
import sys; sys.path.insert(0, "idp"); from office import ticket_queue, cognitive_load, CONCERNS, OFFICE_KITS
for cap in (4, 5, 6):
    waits, left = ticket_queue([5] * 20, cap)
    print(f"office does {cap}/day тЖТ average wait {sum(waits) / len(waits):.1f} days ┬╖ longest {max(waits)} ┬╖ still waiting {left}")
waits, left = ticket_queue([5] * 10 + [8] * 5 + [5] * 5, 5)
print(f"a busy week (8/day) with capacity 5 тЖТ longest wait {max(waits)} days ┬╖ still waiting {left} at the end")
team, kits = cognitive_load(CONCERNS, OFFICE_KITS - {"backups"})
print(f"no backups kit тЖТ the team keeps {len(team)}: {team}")
EOF
python3 idp/test_idp.py

тЬЕ Verify тАФ what you should see

why prints:

тФАтФА to open one classroom (ship one service) a teacher's team must handle 14 concerns alone
   with the facilities office: the team keeps 4 (application code, tests, alerts, on-call for the service)
тФАтФА the ticket queue: 5 requests a day, the office finishes 4 a day, for 20 working days
   finished 80 ┬╖ still waiting 20 ┬╖ average wait 2.0 days
   a ticket finished on day 1 waited 0 days ┬╖ one finished on day 20 waited 4 days тАФ the line grows by 1 every day

Your snippet prints:

office does 4/day тЖТ average wait 2.0 days ┬╖ longest 4 ┬╖ still waiting 20
office does 5/day тЖТ average wait 0.0 days ┬╖ longest 0 ┬╖ still waiting 0
office does 6/day тЖТ average wait 0.0 days ┬╖ longest 0 ┬╖ still waiting 0
a busy week (8/day) with capacity 5 тЖТ longest wait 3 days ┬╖ still waiting 15 at the end
no backups kit тЖТ the team keeps 5: ['application code', 'tests', 'alerts', 'backups', 'on-call for the service']

The tests end with 12/12 passed.

ЁЯПБ What you just proved

With capacity 5 the line never forms. With capacity 4 тАФ one request a day short тАФ the line grows by one every day and never recovers. One busy week at 8 a day leaves 15 tickets waiting even after the rush ends. Hiring more clerks moves the wall; it does not remove it, because requests grow with the school. Self-service removes the line itself. And every kit the office does not offer lands back on the team (backups above).

ЁЯУП Teaching model: this queue has fixed arrivals and fixed capacity. Real request streams are bursty, which makes waits worse than this model shows, not better.

тЪая╕П Common mistakes

ЁЯПн In production

Before you build anything, measure the queue you already have. On a real account, most ticket systems can export requests; a first look needs only three columns:

# tickets.csv: created,resolved,type   (from Jira, ServiceNow, GitHub issues тАж)
python3 -c "
import csv, datetime as d, statistics as s, collections as c
rows = list(csv.DictReader(open('tickets.csv')))
wait = c.defaultdict(list)
for r in rows:
    t = d.datetime.fromisoformat
    wait[r['type']].append((t(r['resolved']) - t(r['created'])).days)
for k, v in sorted(wait.items(), key=lambda kv: -len(kv[1])):
    print(f'{k:<20} {len(v):>4} tickets ┬╖ median wait {s.median(v)} days')
"

The types at the top of that list тАФ "new database", "new repo", "DNS record", "firewall change" тАФ are the first kits to build.

ЁЯПн Why this matters in production: a platform is justified by the wait and the repeated work it removes. Write both down before you start, so you can show later whether the office helped (lesson 11).

тПня╕П Next

You know why. The next question is what to build first тАФ and the answer comes from the teachers, not from the office.

git checkout lesson-02-platform-as-product
тЖР Course homeall lessonsNext тЖТplatform as product

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