ЁЯПл The SchoolтА║ЁЯПЫя╕П Software ArchitectureтА║ЁЯМ│ рдзрдбрд╛ 12 тАФ Evolving architecture рдЖрдгрд┐ рд╕рдВрдкреВрд░реНрдг рдЪрд┐рддреНрд░: рд╡рд╛рдврд╛, рд╕рдбреВ рдирдХрд╛
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯМ│ рдзрдбрд╛ 12 тАФ Evolving architecture рдЖрдгрд┐ рд╕рдВрдкреВрд░реНрдг рдЪрд┐рддреНрд░: рд╡рд╛рдврд╛, рд╕рдбреВ рдирдХрд╛

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 12 ┬╖ рдорд╛рдЧрдЪрд╛: lesson-11-adrs ┬╖ рдкреБрдврдЪрд╛: ЁЯОУ рддреБрдордЪрд╛ рдХреЛрд░реНрд╕ рдкреВрд░реНрдг рдЭрд╛рд▓рд╛


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

рдзрдбреЗ 01тАУ11, рдЖрдгрд┐ рдХрд╛рд│. Architecture рд╣реЗ рдкрд╣рд┐рд▓реНрдпрд╛ term рдЪреЗ рдЪрд┐рддреНрд░ рдирд╛рд╣реА; рдЕрдиреЗрдХ terms рдирдВрддрд░ рдЗрдорд╛рд░рдд рдЬреА рдмрдирддреЗ рддреЗ architecture. рдХрд╛рд│рд╛рдиреБрд╕рд╛рд░ рд╕рдбрдгреЗ рдХрд╕реЗ рдореЛрдЬрд╛рдпрдЪреЗ тАФ cycles, propagation cost рдЖрдгрд┐ рдмрд╛рд╣реЗрд░ рдЬрд╛рдгрд╛рд░реЗ imports тАФ рдЖрдгрд┐ рдореЛрдареА рдЗрдорд╛рд░рдд рд╡рд╛рдкрд░рд╛рдд рдЕрд╕рддрд╛рдирд╛рдЪ strangler fig рдиреЗ рдХрд╢реА рдмрджрд▓рд╛рдпрдЪреА: рд╕рдореЛрд░ рдПрдХ facade, рдПрдХрд╛ рд╡реЗрд│реА рдПрдХ route рд╣рд▓рд╡рд▓рд╛ рдЬрд╛рддреЛ, рдЖрдгрд┐ рд╢реЗрд╡рдЯреА рдЬреБрдиреА рдЗрдорд╛рд░рдд рдирд┐рд╡реГрддреНрдд рд╣реЛрддреЗ. TERMS, strangler() arch/models.py рдордзреНрдпреЗ, propagation_cost() рдЖрдгрд┐ cycles() arch/analyze.py рдордзреНрдпреЗ, evolve() arch/demo.py рдордзреНрдпреЗ.

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

рдкреНрд░рддреНрдпреЗрдХ term рд▓рд╛ рд╢рд╛рд│реЗрддрд▓реА рдЧрд░реНрджреА рдереЛрдбреА рд╡рд╛рдврддреЗ. ЁЯПл рдмрд╛рдВрдзрдХрд╛рдо рдХрд░рдгрд╛рд░реЗ рдЗрдереЗ рдПрдХ рджрд╛рд░, рддрд┐рдереЗ рдПрдХ shortcut рдЬреЛрдбрддрд╛рдд тАФ рддреНрдпрд╛ рддреНрдпрд╛ рджрд┐рд╡рд╢реА рдкреНрд░рддреНрдпреЗрдХ рдпреЛрдЧреНрдпрдЪ рд╡рд╛рдЯрддреЗ. рдХрд╛рд╣реА terms рдирдВрддрд░, рдХреЛрдгрддреНрдпрд╛рд╣реА рдЦреЛрд▓реАрддреВрди рдЬрд╡рд│рдЬрд╡рд│ рдХреЛрдгрддреНрдпрд╛рд╣реА рдЦреЛрд▓реАрдд рдЪрд╛рд▓рдд рдЬрд╛рддрд╛ рдпреЗрддреЗ, рдЕрдиреЗрдХрджрд╛ рдЧреЛрд▓ рдЧреЛрд▓ рдлрд┐рд░рдд. рдЕрд░реНрдзреНрдпрд╛ рд╢рд╛рд│реЗрд▓рд╛ рддреНрд░рд╛рд╕ рджрд┐рд▓реНрдпрд╛рд╢рд┐рд╡рд╛рдп рдХреЛрдгреАрд╣реА рджреБрд░реБрд╕реНрддреАрд╕рд╛рдареА рдПрдХ рдЦреЛрд▓реА рдмрдВрдж рдХрд░реВ рд╢рдХрдд рдирд╛рд╣реА. рдпрд╛рд▓рд╛рдЪ big ball of mud (рдЪрд┐рдЦрд▓рд╛рдЪрд╛ рдЧреЛрд│рд╛) рдореНрд╣рдгрддрд╛рдд.

рдХрддрд░рд┐рдирд╛ рдкреНрд░рддреНрдпреЗрдХ term рд▓рд╛ рдЗрдорд╛рд░рдд рдореЛрдЬрддреЗ тАФ рдХрд┐рддреА рд╡рд░реНрддреБрд│реЗ, рдкреНрд░рддреНрдпреЗрдХ рдЦреЛрд▓реА рдХрд┐рддреА рдЦреЛрд▓реНрдпрд╛рдВрдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪреВ рд╢рдХрддреЗ тАФ рдореНрд╣рдгрдЬреЗ рдЪрд┐рдЦрд▓ рдпреЗрддрд╛рдирд╛ рддрд┐рд▓рд╛ рдлрдХреНрдд рдЬрд╛рдгрд╡рдд рдирд╛рд╣реА, рддрд░ рджрд┐рд╕рддреЛ.

рдЖрдгрд┐ рдЗрдорд╛рд░рддреАрдЪрд╛ рдПрдЦрд╛рджрд╛ рднрд╛рдЧ рджреБрд░реБрд╕реНрдд рдХрд░рдгреНрдпрд╛рдкрд▓реАрдХрдбреЗ рдЬреБрдирд╛ рдЭрд╛рд▓рд╛ рддрд░? рддреА рд╕реЛрдорд╡рд╛рд░реА рд╕рдЧрд│реЗ рдкрд╛рдбреВрди рдЯрд╛рдХрдд рдирд╛рд╣реА. рддреА рдЬреБрдиреНрдпрд╛ рдЗрдорд╛рд░рддреАрд╢реЗрдЬрд╛рд░реА рдирд╡реАрди рджрд╛рд▓рди рдмрд╛рдВрдзрддреЗ, рдкреБрдврдЪреНрдпрд╛ рджрд╛рд░рд╛рд╡рд░ рдПрдХ рд╕реНрд╡рд╛рдЧрддрд┐рдХрд╛ рдмрд╕рд╡рддреЗ, рдЖрдгрд┐ рдПрдХрд╛ рд╡реЗрд│реА рдПрдХ рд╡рд░реНрдЧ рд╣рд▓рд╡рддреЗ: "timetable рдЪреЗ рдкреНрд░рд╢реНрди тАФ рдХреГрдкрдпрд╛ рдирд╡реАрди рджрд╛рд▓рдирд╛рдд." рд╢реЗрд╡рдЯрдЪрд╛ рд╡рд░реНрдЧ рд╣рд▓рд▓рд╛ рдХреА рдЬреБрдиреА рдЗрдорд╛рд░рдд рдкрд╛рдбрд▓реА рдЬрд╛рддреЗ. рдЬреБрдиреНрдпрд╛ рдЭрд╛рдбрд╛рднреЛрд╡рддреА рд╡рд╛рдврдд рд╡рд╛рдврдд рд╕реНрд╡рддрдГрдЪреНрдпрд╛ рдкрд╛рдпрд╛рд╡рд░ рдЙрднреНрдпрд╛ рд░рд╛рд╣рдгрд╛рд▒реНрдпрд╛ strangler fig ЁЯМ│ рдЭрд╛рдбрд╛рд╕рд╛рд░рдЦреЗ.

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

flowchart LR
    subgraph terms["ЁЯУИ the building over four terms"]
      t1["term 1 ┬╖ first plan<br/>0 cycles ┬╖ 24.3%"] --> t2["term 2 ┬╖ as shipped<br/>1 cycle ┬╖ 31.4%"]
      t2 --> t3["term 3 ┬╖ exam rush<br/>4 cycles ┬╖ 49.7%"]
      t3 --> t4["term 4 ┬╖ checks + library<br/>0 cycles ┬╖ 22.2%"]
    end
    subgraph fig["ЁЯМ│ strangler fig"]
      door["ЁЯЫОя╕П facade / router"] -->|"moved routes"| newwing["ЁЯЖХ new wing"]
      door -->|"the rest"| old["ЁЯПЪя╕П old building"]
    end

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдХреЛрдгрддрд╛рд╣реА рдЖрд░рд╛рдЦрдбрд╛ рдЪрд╛рд░ terms рди рдмрджрд▓рддрд╛ рдЯрд┐рдХрдд рдирд╛рд╣реА тАФ рдЖрдгрд┐ big-bang rewrites рдЕрдиреЗрдХрджрд╛ рдЕрдкрдпрд╢реА рдард░рддрд╛рдд, рдорд╣рд┐рдиреЛрдиреНрдорд╣рд┐рдиреЗ feature рдЪреЗ рдХрд╛рдо рдерд╛рдВрдмрд╡рддрд╛рдд, рдЖрдгрд┐ рддреНрдпрд╛рдВрдирд╛ рд╕рддрдд рд╣рд▓рдгрд╛рд░реЗ рд▓рдХреНрд╖реНрдп рдЧрд╛рдард╛рд╡реЗ рд▓рд╛рдЧрддреЗ. рдХрд╛рд│рд╛рдиреБрд╕рд╛рд░ рдореЛрдЬрд▓реНрдпрд╛рдиреЗ "code рд╡рд╛рдИрдЯ рд╡рд╛рдЯрддреЛрдп" рдпрд╛рдЪреЗ рд░реВрдкрд╛рдВрддрд░ рджрд╛рдЦрд╡рддрд╛ рдпреЗрдгрд╛рд▒реНрдпрд╛ рдХрд▓рд╛рдд рд╣реЛрддреЗ. Strangler fig "рдкреБрдиреНрд╣рд╛ рд▓рд┐рд╣рд╛" рдЪреЗ рд░реВрдкрд╛рдВрддрд░ рдЕрдиреЗрдХ рдЫреЛрдЯреНрдпрд╛, рдорд╛рдЧреЗ рдШреЗрддрд╛ рдпреЗрдгрд╛рд▒реНрдпрд╛ рдкрд╛рдпрд▒реНрдпрд╛рдВрдд рдХрд░рддреЗ, рдкреНрд░рддреНрдпреЗрдХ рдкрд╛рдпрд░реА рдореВрд▓реНрдп рджреЗрддреЗ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХреАрд╡рд░ рдерд╛рдВрдмрддрд╛ рдпреЗрддреЗ.

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

arch/models.py рдордзрд▓реЗ TERMS sample рдЪреНрдпрд╛ рдЪрд╛рд░ snapshots рдЪреЗ рд╡рд░реНрдгрди рдХрд░рддреЗ: term 1 рдореНрд╣рдгрдЬреЗ рдзрдбреНрдпрд╛рдВрддреАрд▓ fixes рд▓рд╛рдЧреВ рдХреЗрд▓реЗрд▓рд╛ рдЖрд░рд╛рдЦрдбрд╛, term 2 рдореНрд╣рдгрдЬреЗ рджрд┐рд▓реЗрд▓рд╛ code, term 3 exam-rush рдЪреЗ рдкрд╛рдЪ shortcuts рдЬреЛрдбрддреЗ, term 4 fixes рд▓рд╛рдЧреВ рдХрд░рддреЗ рдЖрдгрд┐ рдПрдХ library рд╡рд┐рднрд╛рдЧ рдЬреЛрдбрддреЗ (LIBRARY: рджреЛрди рдирд╡реАрди рдЦреЛрд▓реНрдпрд╛, рдкрд╛рдЪ рдирд╡реАрди рдорд╛рд░реНрдЧрд┐рдХрд╛, рд╕рдЧрд│реНрдпрд╛ рдЖрддрд▓реНрдпрд╛ рджрд┐рд╢реЗрдиреЗ). evolve() рдкреНрд░рддреНрдпреЗрдХрд╛рд╕рд╛рдареА рдЫрд╛рдкрддреЗ: рдЦреЛрд▓реНрдпрд╛, рдорд╛рд░реНрдЧрд┐рдХрд╛, cycles (рдЖрдгрд┐ рд╕рд░реНрд╡рд╛рдд рдореЛрдард╛), propagation_cost(), рдмрд╛рд╣реЗрд░ рдЬрд╛рдгрд╛рд░реЗ imports рдЖрдгрд┐ рдХрд┐рддреА рдордЬрд▓реЗ рдПрдХрдореЗрдХрд╛рдВрдд рдЧреБрдВрддрд▓реЗ рдЖрд╣реЗрдд. strangler(order) рдкреНрд░рддреНрдпреЗрдХ рдкрд╛рдпрд░реАрд▓рд╛ рдПрдХ route рд╣рд▓рд╡рддреЗ рдЖрдгрд┐ рдирд╡реАрди рджрд╛рд▓рди рдЖрддрд╛ traffic рдЪрд╛ (TRAFFIC, рдПрдХ рд╢рд┐рдХрд╡рдгреНрдпрд╛рд╕рд╛рдареАрдЪреЗ рдЧреГрд╣реАрддрдХ) рдХрд┐рддреА рд╡рд╛рдЯрд╛ рд╣рд╛рддрд╛рд│рддреЗ рддреНрдпрд╛рдЪреА рдмреЗрд░реАрдЬ рдХрд░рддреЗ.

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

python3 arch/demo.py evolve
python3 - <<'EOF'
import sys; sys.path.insert(0, "arch"); from analyze import scan, apply, cycles
from models import TERMS, strangler
g = scan().imports
rush = apply(g, add=TERMS[2][1])
for c in cycles(rush): print("tangled together:", ", ".join(c))
for k, r, n, pct in strangler(("/report-card", "/timetable", "/fees", "/marks/upload", "/enrol", "/admin")):
    print(f"busiest first, step {k}: {r:<14} {pct:>3}% of traffic on the new wing")
EOF
python3 arch/demo.py
python3 arch/test_arch.py

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

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

тФАтФА the building over four terms: rooms ┬╖ corridors ┬╖ cycles (largest) ┬╖ propagation cost ┬╖ outward imports ┬╖ floors tangled
   term 1 ┬╖ the first plan          13 ┬╖ 19 ┬╖ 0 (0) ┬╖ 24.3% ┬╖ 0 ┬╖ floors tangled 0
   term 2 ┬╖ as shipped              13 ┬╖ 24 ┬╖ 1 (2) ┬╖ 31.4% ┬╖ 3 ┬╖ floors tangled 4
   term 3 ┬╖ exam rush               13 ┬╖ 29 ┬╖ 4 (3) ┬╖ 49.7% ┬╖ 3 ┬╖ floors tangled 4
   term 4 ┬╖ checks + a library wing 15 ┬╖ 24 ┬╖ 0 (0) ┬╖ 22.2% ┬╖ 0 ┬╖ floors tangled 0
тФАтФА the strangler fig: a facade sends each route to the old building or the new wing, one route at a time
   step 1: /admin         тЖТ 1/6 routes,   2% of traffic on the new wing
   step 6: /report-card   тЖТ 6/6 routes, 100% of traffic on the new wing

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

tangled together: domain.grades, domain.ports, infra.db
tangled together: app.enrol, infra.email
tangled together: domain.admissions, domain.fees
tangled together: domain.timetable, web.format
busiest first, step 1: /report-card    45% of traffic on the new wing
busiest first, step 2: /timetable      70% of traffic on the new wing
busiest first, step 3: /fees           85% of traffic on the new wing
busiest first, step 4: /marks/upload   93% of traffic on the new wing
busiest first, step 5: /enrol          98% of traffic on the new wing
busiest first, step 6: /admin         100% of traffic on the new wing

рдкреВрд░реНрдг demo тЬЕ done тАФ the floor plan holds рдиреЗ рд╕рдВрдкрддреЛ, рдЖрдгрд┐ tests 12/12 passed рдиреЗ.

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

Exam rush рдордзрд▓реНрдпрд╛ рдпреЛрдЧреНрдп рджрд┐рд╕рдгрд╛рд▒реНрдпрд╛ рдкрд╛рдЪ shortcuts рдиреА рдЗрдорд╛рд░рдд 1 cycle рд╡рд░реВрди 4 рд╡рд░ рдиреЗрд▓реА, рдЖрдгрд┐ propagation cost 31.4% рд╡рд░реВрди 49.7% рд╡рд░ тАФ рд╕рд░рд╛рд╕рд░реА, рдПрдХ рдЦреЛрд▓реА рдЖрддрд╛ рдЬрд╡рд│рдЬрд╡рд│ рдЕрд░реНрдзреНрдпрд╛ рдЗрдорд╛рд░рддреАрдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрддреЗ. Ring рдирд┐рдпрдо рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЛ рдЕрд╕реЗ shortcuts рд╕реБрджреНрдзрд╛ (рдЖрддрд▓реНрдпрд╛ рджрд┐рд╢реЗрдиреЗ, рдХрд┐рдВрд╡рд╛ рдПрдХрд╛рдЪ ring рдЪреНрдпрд╛ рдЖрдд, рдореНрд╣рдгреВрди рдмрд╛рд╣реЗрд░ рдЬрд╛рдгрд╛рд▒реНрдпрд╛ imports рдЪреА рд╕рдВрдЦреНрдпрд╛ 3 рд╡рд░рдЪ рд░рд╛рд╣рд┐рд▓реА) рдирд╡реАрди рд╡рд░реНрддреБрд│реЗ рдкреВрд░реНрдг рдХрд░рдд рд╣реЛрддреЗ, рдЬрд╕реЗ domain.ports рдиреЗ domain.grades import рдХрд░рдгреЗ: рджрд┐рд╢реЗрдмрджреНрджрд▓рдЪрд╛ рдирд┐рдпрдо рд╣рд╛ cycles рдмрджреНрджрд▓рдЪрд╛ рдирд┐рдпрдо рдирд╕рддреЛ, рдореНрд╣рдгреВрдирдЪ рдзрдбрд╛ 09 рджреЛрдиреНрд╣реА рддрдкрд╛рд╕рддреЛ. Term 4 рд╡рд╛рдврд▓рд╛ тАФ 15 рдЦреЛрд▓реНрдпрд╛, рдирд╡реАрди library рд╡рд┐рднрд╛рдЧ тАФ рдЖрдгрд┐ рддрд░реАрд╣реА рдкрд╣рд┐рд▓реНрдпрд╛ рдЖрд░рд╛рдЦрдбреНрдпрд╛рдкреЗрдХреНрд╖рд╛ рд╕реНрд╡рдЪреНрдЫ рд╕рдВрдкрд▓рд╛ (22.2%, 0 cycles): рд╡рд╛рдв рд╣реА рд╕рдорд╕реНрдпрд╛ рдирд╛рд╣реА, рдкрд╣рд╛рд░рд╛ рдирд╕рд▓реЗрд▓реА рд╡рд╛рдв рд╣реА рд╕рдорд╕реНрдпрд╛ рдЖрд╣реЗ. Strangler рдЪрд╛ рдХреНрд░рдо рд╣реА рд╕реБрджреНрдзрд╛ рдЦрд░реА рдирд┐рд╡рдб рдЖрд╣реЗ: /admin рдЖрдзреА рд╣рд▓рд╡рд▓реНрдпрд╛рдиреЗ рддреБрдореНрд╣реА рд╢рд┐рдХрдд рдЕрд╕рддрд╛рдирд╛ traffic рдЪрд╛ рдлрдХреНрдд 2% рдзреЛрдХреНрдпрд╛рдд рдпреЗрддреЛ; /report-card рдЖрдзреА рд╣рд▓рд╡рд▓реНрдпрд╛рдиреЗ рдПрдХрд╛рдЪ рдкрд╛рдпрд░реАрдд 45% рдирд╡реАрди рджрд╛рд▓рдирд╛рд╡рд░ рдпреЗрддреЛ.

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

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

рдЦрд▒реНрдпрд╛ project рд╡рд░ тАФ strangler facade NGINX config рдЗрддрдХрд╛ рд╕рд╛рдзрд╛ рдЕрд╕реВ рд╢рдХрддреЛ: рд╣рд▓рд╡рд▓реЗрд▓реЗ routes рдирд╡реАрди рджрд╛рд▓рдирд╛рдХрдбреЗ, рдмрд╛рдХреА рд╕рдЧрд│реЗ рдЬреБрдиреНрдпрд╛ рдЗрдорд╛рд░рддреАрдХрдбреЗ:

upstream old_building { server legacy.internal:8080; }
upstream new_wing     { server school-v2.internal:8080; }
server {
    listen 80;
    location /admin     { proxy_pass http://new_wing; }
    location /timetable { proxy_pass http://new_wing; }
    location /          { proxy_pass http://old_building; }   # everything not moved yet
}

рдкреНрд░рддреНрдпреЗрдХ build рд╡рд░ рд░рдЪрдиреЗрдЪреЗ metrics рдиреЛрдВрджрд╡рд╛, рдореНрд╣рдгрдЬреЗ рдкреНрд░рддреНрдпреЗрдХ term рдЪрд╛ рдЖрд▓реЗрдЦ рдХрд╛рдврддрд╛ рдпреЗрдИрд▓:

python3 arch/analyze.py | head -1 >> docs/architecture/metrics.log

Cloud рдордзреНрдпреЗ рд╣рд╛рдЪ facade рдЕрдиреЗрдХрджрд╛ API gateway рдХрд┐рдВрд╡рд╛ load balancer rule рдЕрд╕рддреЛ (path-based routing); рдПрдХрд╛рдЪ codebase рдЪреНрдпрд╛ рдЖрдд, рддреЛ рджреЛрди adapters рдЕрд╕рд▓реЗрд▓рд╛ port рдЖрдгрд┐ рддреНрдпрд╛рдВрдЪреНрдпрд╛рдд рдмрджрд▓ рдХрд░рдгрд╛рд░рд╛ feature flag рдЕрд╕рддреЛ.

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рддреАрди рдЖрдХрдбреЗ рдирд┐рд╡рдбрд╛ тАФ cycles, propagation cost, рдирд┐рдпрдорд╛рдВрдЪреА рдЙрд▓реНрд▓рдВрдШрдиреЗ тАФ рдкреНрд░рддреНрдпреЗрдХ merge рд╡рд░ рддреЗ рдиреЛрдВрджрд╡рд╛, рдЖрдгрд┐ рдорд╣рд┐рдиреНрдпрд╛рддреВрди рдПрдХрджрд╛ рдХрд▓ рдкрд╛рд╣рд╛. рдЪрд┐рдЦрд▓ рд╣рд│реВрд╣рд│реВ рд╕рд╛рдЪрддреЛ; рддреЛ рд▓рдХреНрд╖рд╛рдд рдпреЗрдгреЗрд╣реА рд╣рд│реВрдЪ рд╣реЛрддреЗ.

ЁЯОУ рдЗрдорд╛рд░рддреАрдЪрд╛ рдирдХрд╛рд╢рд╛ рдЯрд┐рдХреВрди рд░рд╛рд╣рддреЛ

Architecture рдореНрд╣рдгрдЬреЗ рдмрджрд▓рд╛рдпрд▓рд╛ рдорд╣рд╛рдЧ рдЕрд╕рд▓реЗрд▓реЗ рдирд┐рд░реНрдгрдп тЖТ рдореЛрдЬрд▓реЗрд▓реНрдпрд╛, рдереЛрдбреНрдпрд╛ рдЖрдгрд┐ рдЖрд╡рд╢реНрдпрдХ рдорд╛рд░реНрдЧрд┐рдХрд╛ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдЦреЛрд▓реНрдпрд╛ тЖТ рдЬрд┐рдиреЗ рдПрдХрд╛рдЪ рджрд┐рд╢реЗрдиреЗ рдЬрд╛рдгрд╛рд░реЗ рдордЬрд▓реЗ тЖТ рдордзреНрдпрднрд╛рдЧреА, рджрд╛рд░реЗ рдмрд╛рд╣реЗрд░рдЪреНрдпрд╛ рдмрд╛рдЬреВрд▓рд╛ рдЕрд╕рд▓реЗрд▓реА рдирд┐рдпрдордкреБрд╕реНрддрд┐рдХрд╛ тЖТ рдЖрдкрд╛рдкрд▓реЗ рд╢рдмреНрдж рдЕрд╕рд▓реЗрд▓реЗ рд╡рд┐рднрд╛рдЧ тЖТ рдХреЕрдореНрдкрд╕рдЪреНрдпрд╛ рдЖрдзреА рднрд┐рдВрддреА рдЕрд╕рд▓реЗрд▓реА рдПрдХрдЪ рдЗрдорд╛рд░рдд тЖТ рдорд╛рд░реНрдЧрд┐рдХреЗрддреВрди рдлреЗрд▒реНрдпрд╛рдВрдРрд╡рдЬреА рдлрд▓рдХрд╛рд╡рд░ рд╕реВрдЪрдирд╛ тЖТ рдкреНрд░рддреНрдпреЗрдХ рдХрдкрд╛рдЯрд╛рд▓рд╛ рдПрдХрдЪ рдорд╛рд▓рдХ тЖТ рдкреНрд░рддреНрдпреЗрдХ push рд╡рд░ рдПрдХ рдирд┐рд░реАрдХреНрд╖рдХ тЖТ рдЗрдорд╛рд░рддреАрддреВрдирдЪ рдЫрд╛рдкрд▓реЗрд▓рд╛ рдЗрдорд╛рд░рддреАрдЪрд╛ рдирдХрд╛рд╢рд╛ тЖТ "рдХрд╛" рдЪреА рдиреЛрдВрджрд╡рд╣реА тЖТ рдЖрдгрд┐ рджрд░ term рд▓рд╛ рд╡рд╛рдвреВрдирд╣реА рдЪрд┐рдЦрд▓ рди рд╣реЛрдгрд╛рд░реА рдЗрдорд╛рд░рдд. рддреБрдореНрд╣реА рдлрдХреНрдд software architecture рд╢рд┐рдХрд▓рд╛ рдирд╛рд╣реАрдд тАФ рддреБрдореНрд╣реА рдХреЛрдгрддрд╛рд╣реА codebase рдЙрдШрдбреВ рд╢рдХрддрд╛, рддреНрдпрд╛рдЪреНрдпрд╛ рдЦреЛрд▓реНрдпрд╛ рдЖрдгрд┐ рдорд╛рд░реНрдЧрд┐рдХрд╛ рдХрд╛рдвреВ рд╢рдХрддрд╛, рдЖрдгрд┐ рдХреЛрдгрддреЗ рдирд┐рд░реНрдгрдп рдорд╣рд╛рдЧ рдЖрд╣реЗрдд, рддреЛ рдХреЛрдгрддреЗ рдирд┐рдпрдо рдореЛрдбрддреЛ, рдЖрдгрд┐ рдЖрдзреА рдХрд╛рдп рджреБрд░реБрд╕реНрдд рдХрд░рд╛рдпрдЪреЗ рддреЗ рд╕рд╛рдВрдЧреВ рд╢рдХрддрд╛. ЁЯПЫя╕ПЁЯУРЁЯОУ

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

рдЗрддрд░ рд╢рд╛рд│рд╛. System Design рд╢рд╛рд│рд╛ caches, queues рдЖрдгрд┐ shards рд╕рд╣ distributed systems design рдХрд░рддреЗ; Distributed Systems рд╢рд╛рд│рд╛ machines рдордзреНрдпреЗ рдХрд╛рдп рдмрд┐рдШрдбрддреЗ рддреЗ рд╕рдордЬрд╛рд╡рддреЗ; API рд╢рд╛рд│рд╛ рд╡рд┐рднрд╛рдЧрд╛рдВрдордзрд▓реЗ рдХрд░рд╛рд░ design рдХрд░рддреЗ; CI/CD рд╢рд╛рд│рд╛ fitness functions рдЪрд╛рд▓рд╡рддреЗ; School portal рдордзреНрдпреЗ рдмрд╛рдХреА рд╕рдЧрд│реЗ рдЖрд╣реЗ.

git checkout main
python3 arch/demo.py     # one last run, for fun

ЁЯМ│ Lesson 12 тАФ Evolving architecture & the whole picture: grow, don't rot

ЁЯУН You are here: Lesson 12 of 12 ┬╖ Previous: lesson-11-adrs ┬╖ Next: ЁЯОУ you're done


ЁЯУж What's in this branch

Lessons 01тАУ11, plus time. Architecture is not a picture from the first term; it is what the building becomes after many terms. How to measure rot over time тАФ cycles, propagation cost and outward imports тАФ and how to change a big building while it is in use with the strangler fig: a facade in front, routes moved one at a time, the old building retired at the end. TERMS, strangler() in arch/models.py, propagation_cost() and cycles() in arch/analyze.py, evolve() in arch/demo.py.

ЁЯзТ Explain like I'm 5

Every term, the school gets a little busier. ЁЯПл Builders add a door here, a shortcut there тАФ each one sensible on the day. After a few terms, you can walk from any room to almost any other room, often in circles. Nobody can close one room for repairs without disturbing half the school. That is a big ball of mud.

Katrina measures the building every term тАФ how many circles, how many rooms each room can reach тАФ so she can see the mud coming, not just feel it.

And when a part of the building is too old to fix? She does not knock it all down on a Monday. She builds a new wing next to the old building, puts a receptionist at the front door, and moves one class at a time: "timetable questions тАФ new wing, please." When the last class has moved, the old building comes down. Like a strangler fig ЁЯМ│ tree that grows around an old tree until it can stand on its own.

ЁЯЧ║я╕П Diagram

flowchart LR
    subgraph terms["ЁЯУИ the building over four terms"]
      t1["term 1 ┬╖ first plan<br/>0 cycles ┬╖ 24.3%"] --> t2["term 2 ┬╖ as shipped<br/>1 cycle ┬╖ 31.4%"]
      t2 --> t3["term 3 ┬╖ exam rush<br/>4 cycles ┬╖ 49.7%"]
      t3 --> t4["term 4 ┬╖ checks + library<br/>0 cycles ┬╖ 22.2%"]
    end
    subgraph fig["ЁЯМ│ strangler fig"]
      door["ЁЯЫОя╕П facade / router"] -->|"moved routes"| newwing["ЁЯЖХ new wing"]
      door -->|"the rest"| old["ЁЯПЪя╕П old building"]
    end

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

тЭУ What

ЁЯдФ Why

Because no plan survives four terms unchanged тАФ and big-bang rewrites fail often, stop feature work for months, and have to hit a moving target. Measuring over time turns "the code feels worse" into a trend you can show. The strangler fig turns "rewrite it" into many small, reversible steps, each one delivering value and each one able to stop.

ЁЯФз How (in this repo)

TERMS in arch/models.py describes four snapshots of the sample: term 1 is the plan with the lesson fixes applied, term 2 is the shipped code, term 3 adds five exam-rush shortcuts, term 4 applies the fixes and adds a library department (LIBRARY: two new rooms, five new corridors, all pointing inward). evolve() prints, for each: rooms, corridors, cycles (and the largest), propagation_cost(), outward imports and how many floors are tangled together. strangler(order) moves one route per step and adds up the share of traffic (TRAFFIC, a teaching assumption) the new wing now serves.

ЁЯзк Try it

python3 arch/demo.py evolve
python3 - <<'EOF'
import sys; sys.path.insert(0, "arch"); from analyze import scan, apply, cycles
from models import TERMS, strangler
g = scan().imports
rush = apply(g, add=TERMS[2][1])
for c in cycles(rush): print("tangled together:", ", ".join(c))
for k, r, n, pct in strangler(("/report-card", "/timetable", "/fees", "/marks/upload", "/enrol", "/admin")):
    print(f"busiest first, step {k}: {r:<14} {pct:>3}% of traffic on the new wing")
EOF
python3 arch/demo.py
python3 arch/test_arch.py

тЬЕ Verify тАФ what you should see

evolve prints:

тФАтФА the building over four terms: rooms ┬╖ corridors ┬╖ cycles (largest) ┬╖ propagation cost ┬╖ outward imports ┬╖ floors tangled
   term 1 ┬╖ the first plan          13 ┬╖ 19 ┬╖ 0 (0) ┬╖ 24.3% ┬╖ 0 ┬╖ floors tangled 0
   term 2 ┬╖ as shipped              13 ┬╖ 24 ┬╖ 1 (2) ┬╖ 31.4% ┬╖ 3 ┬╖ floors tangled 4
   term 3 ┬╖ exam rush               13 ┬╖ 29 ┬╖ 4 (3) ┬╖ 49.7% ┬╖ 3 ┬╖ floors tangled 4
   term 4 ┬╖ checks + a library wing 15 ┬╖ 24 ┬╖ 0 (0) ┬╖ 22.2% ┬╖ 0 ┬╖ floors tangled 0
тФАтФА the strangler fig: a facade sends each route to the old building or the new wing, one route at a time
   step 1: /admin         тЖТ 1/6 routes,   2% of traffic on the new wing
   step 6: /report-card   тЖТ 6/6 routes, 100% of traffic on the new wing

Your snippet prints:

tangled together: domain.grades, domain.ports, infra.db
tangled together: app.enrol, infra.email
tangled together: domain.admissions, domain.fees
tangled together: domain.timetable, web.format
busiest first, step 1: /report-card    45% of traffic on the new wing
busiest first, step 2: /timetable      70% of traffic on the new wing
busiest first, step 3: /fees           85% of traffic on the new wing
busiest first, step 4: /marks/upload   93% of traffic on the new wing
busiest first, step 5: /enrol          98% of traffic on the new wing
busiest first, step 6: /admin         100% of traffic on the new wing

The full demo ends with тЬЕ done тАФ the floor plan holds, and the tests with 12/12 passed.

ЁЯПБ What you just proved

Five sensible-looking shortcuts in the exam rush took the building from 1 cycle to 4, and propagation cost from 31.4% to 49.7% тАФ on average, a room now reaches about half the building. Even shortcuts the ring rule allows (inward, or inside one ring, so the outward-import count stayed at 3) closed new circles, like domain.ports importing domain.grades: a rule about direction is not a rule about cycles, which is why lesson 09 checks both. Term 4 grew тАФ 15 rooms, a new library department тАФ and still ended cleaner than the first plan (22.2%, 0 cycles): growth is not the problem, unguarded growth is. The strangler order is a real choice too: moving /admin first puts only 2% of traffic at risk while you learn; moving /report-card first puts 45% on the new wing in one step.

тЪая╕П Common mistakes

ЁЯПн In production

On a real project тАФ a strangler facade can be as simple as an NGINX config: moved routes go to the new wing, everything else to the old building:

upstream old_building { server legacy.internal:8080; }
upstream new_wing     { server school-v2.internal:8080; }
server {
    listen 80;
    location /admin     { proxy_pass http://new_wing; }
    location /timetable { proxy_pass http://new_wing; }
    location /          { proxy_pass http://old_building; }   # everything not moved yet
}

Record the structure metrics on every build so you can plot them per term:

python3 arch/analyze.py | head -1 >> docs/architecture/metrics.log

In the cloud, the same facade is often an API gateway or a load balancer rule (path-based routing); inside one codebase, it is a port with two adapters and a feature flag to switch between them.

ЁЯПн Why this matters in production: pick three numbers тАФ cycles, propagation cost, rule violations тАФ record them on every merge, and look at the trend once a month. Mud is slow; so is noticing it.

ЁЯОУ The floor plan holds

Architecture is the decisions that are costly to change тЖТ rooms with few, needed corridors, measured тЖТ floors whose stairs go one way тЖТ the rule book in the middle with its doors facing out тЖТ departments with their own words тЖТ one building with walls before a campus тЖТ notices on the board instead of walks down the corridor тЖТ every cupboard with one owner тЖТ an inspector on every push тЖТ a floor plan printed from the building тЖТ a logbook of why тЖТ and a building that grows every term without turning into mud. You didn't just learn software architecture тАФ you can open any codebase, draw its rooms and corridors, and say which decisions are expensive, which rules it breaks, and what to fix first. ЁЯПЫя╕ПЁЯУРЁЯОУ

тПня╕П Next

The other schools. The System Design school designs distributed systems with caches, queues and shards; the Distributed Systems school explains what goes wrong between machines; the API school designs the contracts between departments; the CI/CD school runs the fitness functions; the School portal has the rest.

git checkout main
python3 arch/demo.py     # one last run, for fun
тЖР PreviousadrsFinished! Take the quiz тЖТcheck what stuck

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