ЁЯПл The SchoolтА║ЁЯУР System DesignтА║ЁЯТ░ рдзрдбрд╛ 16 тАФ Cost, trade-offs рдЖрдгрд┐ ADRs: рдкреНрд░рддреНрдпреЗрдХ box рд▓рд╛ рдПрдХ рдмрд┐рд▓ рдЖрдгрд┐ рдПрдХ рдХрд╛рд░рдг рдЕрд╕рддреЗ
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯТ░ рдзрдбрд╛ 16 тАФ Cost, trade-offs рдЖрдгрд┐ ADRs: рдкреНрд░рддреНрдпреЗрдХ box рд▓рд╛ рдПрдХ рдмрд┐рд▓ рдЖрдгрд┐ рдПрдХ рдХрд╛рд░рдг рдЕрд╕рддреЗ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 18 рдкреИрдХреА рдзрдбрд╛ 16 ┬╖ рдкреБрдвреЗ: lesson-17-designs-short-notify-chat


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

рдзрдбреЗ 01тАУ15, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ box рд╕рд╛рдареА рд╢реЗрд╡рдЯрдЪреЗ рджреЛрди рдкреНрд░рд╢реНрди: рдпрд╛рдЪрд╛ рдЦрд░реНрдЪ рдХрд┐рддреА? рдЖрдгрд┐ рд╣реЗ рдЗрдереЗ рдХрд╛ рдЖрд╣реЗ? рддреБрдореНрд╣реА рдЙрджрд╛рд╣рд░рдгрд╛рджрд╛рдЦрд▓ рдХрд┐рдорддреАрдВрд╕рд╣ рдПрдХ рдкрд╣рд┐рд▓реЗ рдмрд┐рд▓ рдмрдирд╡рддрд╛, рддреНрдпрд╛рдд рдХреЛрдгрддрд╛ рднрд╛рдЧ рд╕рд░реНрд╡рд╛рдд рдореЛрдард╛ рддреЗ рд╢реЛрдзрддрд╛, рдПрдХ trade-off table рдареЗрд╡рддрд╛, рдЖрдгрд┐ рдПрдХ ADR (Architecture Decision Record) рд▓рд┐рд╣рд┐рддрд╛: context, decision, consequences, status тАФ рдЖрдгрд┐ рддреЗ рд▓рд┐рд╣рд┐рдгреЗ рдХрдзреА рдпреЛрдЧреНрдп рддреЗ рд╢рд┐рдХрддрд╛.

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

рдЖрд░рд╛рдЦрдбрд╛ рднрд┐рдВрддреАрд╡рд░ рд▓рд╛рд╡рд▓реЗрд▓рд╛ рдЖрд╣реЗ. рд╢рд╛рд│реЗрдЪреНрдпрд╛ рдореБрдЦреНрдпрд╛рдзреНрдпрд╛рдкрд┐рдХрд╛ рдЖрдд рдпреЗрддрд╛рдд рдЖрдгрд┐ рджреЛрди рдкреНрд░рд╢реНрди рд╡рд┐рдЪрд╛рд░рддрд╛рдд.

"рджрд░ рдорд╣рд┐рдиреНрдпрд╛рд▓рд╛ рдпрд╛рдЪрд╛ рдЦрд░реНрдЪ рдХрд┐рддреА рдпреЗрдИрд▓?" рдХрддрд░рд┐рдирд╛ рдУрд│реАрдиреЗ рдУрд│ рдмреЗрд░реАрдЬ рдХрд░рддреЗ: servers, storage, рдмрд╛рд╣реЗрд░ рдкрд╛рдард╡рд▓реЗрд▓рд╛ data, requests. рдЙрджрд╛рд╣рд░рдгрд╛рджрд╛рдЦрд▓ рдХрд┐рдорддреАрдВрдиреА рдорд╣рд┐рдиреНрдпрд╛рд▓рд╛ $1,216. рд╕рд░реНрд╡рд╛рдд рдореЛрдареА рдУрд│ servers рдирд╛рд╣реА. рддреА рдЖрд╣реЗ рдкрд╛рд▓рдХрд╛рдВрдЪреНрдпрд╛ phones рдХрдбреЗ рдмрд╛рд╣реЗрд░ рдкрд╛рдард╡рд▓реЗрд▓рд╛ data.

"рдЖрдгрд┐ рдЗрдереЗ Redis box рдХрд╛ рдЖрд╣реЗ?" рдХреЛрдгрд╛рд▓рд╛рдЪ рдЖрдард╡рдд рдирд╛рд╣реА. рдРрд╢реНрд╡рд░реНрдпрд╛ рдЧреЗрд▓реНрдпрд╛ рд╡рд░реНрд╖реА рдХрд╛рд░реНрдпрд╛рд▓рдп рд╕реЛрдбреВрди рдЧреЗрд▓реА. рдореНрд╣рдгреВрди рджреАрдкрд┐рдХрд╛ рдирд┐рдпрдо рдХрд░рддреЗ: рдкреНрд░рддреНрдпреЗрдХ рдореЛрдареНрдпрд╛ рдирд┐рд░реНрдгрдпрд╛рд▓рд╛ рдПрдХрд╛ folder рдордзреНрдпреЗ рдПрдХ рдкрд╛рди рдорд┐рд│рддреЗ. рдЕрдбрдЪрдг рдХрд╛рдп рд╣реЛрддреА? рдЖрдкрдг рдХрд╛рдп рдирд┐рд╡рдбрд▓реЗ? рддреНрдпрд╛рдЪреА рдЖрдкрд▓реНрдпрд╛рд▓рд╛ рдХрд╛рдп рдХрд┐рдВрдордд рдореЛрдЬрд╛рд╡реА рд▓рд╛рдЧрддреЗ? рддреЗ рдЕрдЬреВрди рдЦрд░реЗ рдЖрд╣реЗ рдХрд╛? рдкреБрдврдЪреНрдпрд╛ рд╡рд░реНрд╖реА рдирд╡реА planner рдЕрдВрджрд╛рдЬ рдмрд╛рдВрдзрдгреНрдпрд╛рдРрд╡рдЬреА рддреЗ рдкрд╛рди рджреЛрди рдорд┐рдирд┐рдЯрд╛рдВрдд рд╡рд╛рдЪрддреЗ.

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

flowchart LR
    subgraph bill["ЁЯз╛ a first bill (example prices)"]
      s["servers $360"]
      st["storage $46"]
      e["egress $450 тЖР largest"]
      r["requests $360"]
      t["total $1,216 / month"]
    end
    bill --> tt["тЪЦя╕П trade-off table<br/>option ┬╖ gain ┬╖ cost ┬╖ risk"]
    tt --> adr["ЁЯУД ADR 001<br/>Status ┬╖ Context ┬╖ Decision ┬╖ Consequences"]
    adr --> repo["ЁЯУБ docs/adr/ in the repo"]

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

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

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рдЬреНрдпрд╛ рд░рдЪрдиреЗрдЪрд╛ рдЦрд░реНрдЪ рдХреЛрдгрд╛рд▓рд╛рдЪ рдкрд░рд╡рдбрдд рдирд╛рд╣реА рддреА рд░рдЪрдирд╛ рдирд╛рд╣реА, рдЖрдгрд┐ рдЬреА рд░рдЪрдирд╛ рдХреЛрдгрд╛рд▓рд╛рдЪ рд╕рдордЬрд╛рд╡рддрд╛ рдпреЗрдд рдирд╛рд╣реА рддреА рд╕рд╛рдВрднрд╛рд│рд▓реА рдЬрд╛рдд рдирд╛рд╣реА. рдзрдбрд╛ 02 рдордзрд▓реЗ рдЖрдХрдбреЗ рдкреНрд░рддреНрдпреЗрдХ box рд▓рд╛ рдХрд┐рдВрдордд рджреЗрддрд╛рдд. ADR рдХрд┐рдорддреАрд╢реЗрдЬрд╛рд░реА рдХрд╛рд░рдг рдареЗрд╡рддреЗ, рдореНрд╣рдгрдЬреЗ рдирдВрддрд░рдЪрд╛ рдмрджрд▓ рддрдереНрдпрд╛рдВрдирд┐рд╢реА рд╣реЛрддреЛ тАФ рдирд┐рд░реБрдкрдпреЛрдЧреА рджрд┐рд╕рдгрд╛рд░рд╛ рдкрдг рдкреНрд░рддреНрдпрдХреНрд╖рд╛рдд p99 рд╕рд╛рдВрднрд╛рд│рдгрд╛рд░рд╛ box delete рдХрд░реВрди рдирд╛рд╣реА.

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

design/designs.py рдордзреНрдпреЗ:

design/demo.py рдордзрд▓реЗ cost() рд╕реВрдЪрдирд╛ рдлрд▓рдХрд╛рдЪреА рдХрд┐рдВрдордд рдХрд╛рдврддреЗ (6 servers, 2,000 GB, 5,000 GB egress, 900 million requests) рдЖрдгрд┐ Redis cache рдмрджреНрджрд▓ ADR 001 рд▓рд┐рд╣рд┐рддреЗ.

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

python3 design/demo.py cost
python3 - <<'EOF'
import sys; sys.path.insert(0, "design"); from designs import monthly_cost, adr
base = dict(servers=6, storage_gb=2_000, egress_gb=5_000, requests_million=900)
for name, change in (("first bill", {}), ("double the servers", {"servers": 12}), ("photos: 10x egress", {"egress_gb": 50_000}), ("5 years of storage", {"storage_gb": 11_000})):
    c = monthly_cost(**{**base, **change}); big = max((k for k in c if k != "total"), key=c.get)
    print(f"{name:<20} total ${c['total']:>9,.2f} ┬╖ biggest part: {big} ${c[big]:,.2f}")
print(adr(2, "Send urgent notices by SMS through a queue", "5M parents; SMS must not slow the post request.",
          "Post writes one job to a queue; 50 workers send in batches.", ["post answers in ~50 ms", "SMS may arrive minutes later", "workers must be idempotent"], status="Proposed"))
EOF

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

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

тФАтФА a first bill for the notice board (example prices)
   servers   $   360.00
   storage   $    46.00
   egress    $   450.00
   requests  $   360.00
   total     $ 1,216.00
тФАтФА the decision, written down:
   # ADR 001: Cache the class feed in Redis

   Status: Accepted

   ## Context
   Reads are 100x writes; p99 must stay under 200 ms at 3x peak.

   ## Decision
   Cache-aside per class, TTL 60 s, delete on post.

   ## Consequences
   - ~80% of reads skip the database (lesson 05's curve)
   - a notice can be up to 60 s late for some readers
   - one more thing to run

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

first bill           total $ 1,216.00 ┬╖ biggest part: egress $450.00
double the servers   total $ 1,576.00 ┬╖ biggest part: servers $720.00
photos: 10x egress   total $ 5,266.00 ┬╖ biggest part: egress $4,500.00
5 years of storage   total $ 1,423.00 ┬╖ biggest part: egress $450.00
# ADR 002: Send urgent notices by SMS through a queue

Status: Proposed

## Context
5M parents; SMS must not slow the post request.

## Decision
Post writes one job to a queue; 50 workers send in batches.

## Consequences
- post answers in ~50 ms
- SMS may arrive minutes later
- workers must be idempotent

Tests рдордзреНрдпреЗ тЬЕ L16 the bill adds up and the ADR has its sections рдЖрд╣реЗ.

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

рдкрд╣рд┐рд▓реЗ рдмрд┐рд▓ рдорд╣рд┐рдиреНрдпрд╛рд▓рд╛ $1,216 рдЖрд╣реЗ, рдЖрдгрд┐ рд╕рд░реНрд╡рд╛рдд рдореЛрдареА рдУрд│ egress ($450) рдЖрд╣реЗ, servers рдирд╛рд╣реАрдд. Servers рджреБрдкреНрдкрдЯ рдХреЗрд▓реНрдпрд╛рдиреЗ $360 рд╡рд╛рдврддрд╛рдд. рдкрд╛рдЪ рд╡рд░реНрд╖рд╛рдВрдЪреЗ storage (11,000 GB тАФ рдзрдбрд╛ 02 рдЪреЗ ~11 TB) рдлрдХреНрдд $207 рд╡рд╛рдврд╡рддреЗ. рдкрдг рдкрд╛рд▓рдХрд╛рдВрдирд╛ photos download рдХрд░реВ рджрд┐рд▓реЗ (10├Ч egress) рддрд░ рдмрд┐рд▓ $5,266 рд╣реЛрддреЗ тАФ рдПрдХрд╛ product рдХрд▓реНрдкрдиреЗрдиреЗ рд╕рдЧрд│реНрдпрд╛ server рдирд┐рд░реНрдгрдпрд╛рдВрдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рдЦрд░реНрдЪ рдмрджрд▓рд▓рд╛. рдореНрд╣рдгреВрдирдЪ рдкреНрд░рддреНрдпреЗрдХ рд░рдЪрдиреЗрдд cost рдЪреА рдУрд│ рд╣рд╡реА. рдЖрдгрд┐ ADR рдЫреЛрдЯреЗ рдЕрд╕рддреЗ: status, context, decision, рдЖрдгрд┐ рд╡рд╛рдИрдЯ рдкрд░рд┐рдгрд╛рдорд╣реА рд╕рд╛рдВрдЧрдгрд╛рд░реЗ consequences ("SMS may arrive minutes later").

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

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

рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ рдорд╛рдЧрдЪреНрдпрд╛ рдорд╣рд┐рдиреНрдпрд╛рдЪреЗ рдмрд┐рд▓ рдЦрд░реЛрдЦрд░ рдХрд┐рддреА рдЖрд╣реЗ рддреЗ, рдкреНрд░рддреНрдпреЗрдХ service рдиреБрд╕рд╛рд░, рдкрд╛рд╣рд╛:

aws ce get-cost-and-usage --time-period Start=2026-08-01,End=2026-09-01 \
    --granularity MONTHLY --metrics UnblendedCost \
    --group-by Type=DIMENSION,Key=SERVICE

forecast рдЪреНрдпрд╛ 80% рд▓рд╛ email рдкрд╛рдард╡рдгрд╛рд░реЗ рдорд╛рд╕рд┐рдХ budget:

aws budgets create-budget --account-id 111122223333 \
    --budget '{"BudgetName":"notice-board","BudgetLimit":{"Amount":"1500","Unit":"USD"},"TimeUnit":"MONTHLY","BudgetType":"COST"}' \
    --notifications-with-subscribers '[{"Notification":{"NotificationType":"FORECASTED","ComparisonOperator":"GREATER_THAN","Threshold":80},"Subscribers":[{"SubscriptionType":"EMAIL","Address":"planning-office@school.example"}]}]'

рдкреНрд░рддреНрдпреЗрдХ resource рд▓рд╛ рддреЗ рд░рдЪрдиреЗрдЪреНрдпрд╛ рдЬреНрдпрд╛ рднрд╛рдЧрд╛рдЪреЗ рдЖрд╣реЗ рддреЛ tag рд▓рд╛рд╡рд╛, рдореНрд╣рдгрдЬреЗ рдмрд┐рд▓ рдЖрдХреГрддреАрдкреНрд░рдорд╛рдгреЗрдЪ рд╡рд┐рднрд╛рдЧрддрд╛ рдпреЗрддреЗ (tag рд▓рд╛ cost allocation tag рдореНрд╣рдгреВрди activate рдХрд░рд╛):

aws resourcegroupstaggingapi tag-resources \
    --resource-arn-list arn:aws:rds:ap-south-1:111122223333:db:notices-db \
    --tags component=notices-db,app=notice-board

repo рдордзреНрдпреЗ files рдореНрд╣рдгреВрди, рдХреНрд░рдорд╛рдВрдХ рджрд┐рд▓реЗрд▓реЗ ADRs:

docs/adr/
  0001-cache-the-class-feed-in-redis.md
  0002-send-urgent-notices-by-sms-through-a-queue.md
  0003-postgresql-as-the-main-store.md

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдкрд╣рд┐рд▓реНрдпрд╛ рддрд┐рдорд╛рд╣реАрдд рджрд░ рдорд╣рд┐рдиреНрдпрд╛рд▓рд╛ рдЦрд░реЗ рдмрд┐рд▓ рдЖрд░рд╛рдЦрдбреНрдпрд╛рддрд▓реНрдпрд╛ рдмрд┐рд▓рд╛рд╢реА рддрд╛рдбреВрди рдкрд╛рд╣рд╛. рдЕрдВрджрд╛рдЬрд╛рдЪреНрдпрд╛ рджреБрдкреНрдкрдЯ рдЕрд╕рд▓реЗрд▓реА рдУрд│ рд╣рд╛ рд░рдЪрдиреЗрдЪрд╛ рдкреНрд░рд╢реНрди рдЖрд╣реЗ тАФ рдЖрдгрд┐ рддреНрдпрд╛рдЪреЗ рдЙрддреНрддрд░ рдмрд╣реБрддреЗрдХрджрд╛ рдПрдХ ADR рдЕрд╕рддреЗ.

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

рдкрджреНрдзрдд рдкреВрд░реНрдг рдЭрд╛рд▓реА: рдЧрд░рдЬрд╛, рдЖрдХрдбреЗ, API, data, blocks, failure, security, observability, cost, ADRs. рдЖрддрд╛ рддреА рд╕реБрд░реБрд╡рд╛рддреАрдкрд╛рд╕реВрди рд╢реЗрд╡рдЯрдкрд░реНрдпрдВрдд рд╡рд╛рдкрд░рд╛. рд╕реЛрдбрд╡рд▓реЗрд▓реНрдпрд╛ рд░рдЪрдирд╛: URL shortener, notifications рдЖрдгрд┐ chat.

git checkout lesson-17-designs-short-notify-chat

ЁЯТ░ Lesson 16 тАФ Cost, trade-offs & ADRs: every box has a bill and a reason

ЁЯУН You are here: Lesson 16 of 18 ┬╖ Next: lesson-17-designs-short-notify-chat


ЁЯУж What's in this branch

Lessons 01тАУ15, plus the last two questions for every box: what does it cost? and why is it here? You build a first bill with example prices, find what dominates it, keep a trade-off table, and write an ADR (Architecture Decision Record): context, decision, consequences, status тАФ and learn when one is worth writing.

ЁЯзТ Explain like I'm 5

The plan is on the wall. The head of the school walks in and asks two questions.

"How much will it cost each month?" Katrina adds it up line by line: the servers, the storage, the data sent out, the requests. $1,216 a month, with example prices. The largest line is not the servers. It is the data sent out to parents' phones.

"And why is there a Redis box?" Nobody remembers. Aishwarya left the office last year. So Dipika makes a rule: every big decision gets one page in a folder. What was the problem? What did we choose? What does it cost us? Is it still true? Next year, a new planner reads the page in two minutes instead of guessing.

ЁЯЧ║я╕П Diagram

flowchart LR
    subgraph bill["ЁЯз╛ a first bill (example prices)"]
      s["servers $360"]
      st["storage $46"]
      e["egress $450 тЖР largest"]
      r["requests $360"]
      t["total $1,216 / month"]
    end
    bill --> tt["тЪЦя╕П trade-off table<br/>option ┬╖ gain ┬╖ cost ┬╖ risk"]
    tt --> adr["ЁЯУД ADR 001<br/>Status ┬╖ Context ┬╖ Decision ┬╖ Consequences"]
    adr --> repo["ЁЯУБ docs/adr/ in the repo"]

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

тЭУ What

ЁЯдФ Why

Because a design that nobody can pay for is not a design, and a design nobody can explain is not maintained. Numbers from lesson 02 give each box a price. The ADR keeps the reason next to the price, so a later change is made with the facts тАФ not by deleting a box that looked useless and was in fact holding up the p99.

ЁЯФз How (in this repo)

In design/designs.py:

cost() in design/demo.py prices the notice board (6 servers, 2,000 GB, 5,000 GB egress, 900 million requests) and writes ADR 001 about the Redis cache.

ЁЯзк Try it

python3 design/demo.py cost
python3 - <<'EOF'
import sys; sys.path.insert(0, "design"); from designs import monthly_cost, adr
base = dict(servers=6, storage_gb=2_000, egress_gb=5_000, requests_million=900)
for name, change in (("first bill", {}), ("double the servers", {"servers": 12}), ("photos: 10x egress", {"egress_gb": 50_000}), ("5 years of storage", {"storage_gb": 11_000})):
    c = monthly_cost(**{**base, **change}); big = max((k for k in c if k != "total"), key=c.get)
    print(f"{name:<20} total ${c['total']:>9,.2f} ┬╖ biggest part: {big} ${c[big]:,.2f}")
print(adr(2, "Send urgent notices by SMS through a queue", "5M parents; SMS must not slow the post request.",
          "Post writes one job to a queue; 50 workers send in batches.", ["post answers in ~50 ms", "SMS may arrive minutes later", "workers must be idempotent"], status="Proposed"))
EOF

тЬЕ Verify тАФ what you should see

cost prints:

тФАтФА a first bill for the notice board (example prices)
   servers   $   360.00
   storage   $    46.00
   egress    $   450.00
   requests  $   360.00
   total     $ 1,216.00
тФАтФА the decision, written down:
   # ADR 001: Cache the class feed in Redis

   Status: Accepted

   ## Context
   Reads are 100x writes; p99 must stay under 200 ms at 3x peak.

   ## Decision
   Cache-aside per class, TTL 60 s, delete on post.

   ## Consequences
   - ~80% of reads skip the database (lesson 05's curve)
   - a notice can be up to 60 s late for some readers
   - one more thing to run

Your snippet prints:

first bill           total $ 1,216.00 ┬╖ biggest part: egress $450.00
double the servers   total $ 1,576.00 ┬╖ biggest part: servers $720.00
photos: 10x egress   total $ 5,266.00 ┬╖ biggest part: egress $4,500.00
5 years of storage   total $ 1,423.00 ┬╖ biggest part: egress $450.00
# ADR 002: Send urgent notices by SMS through a queue

Status: Proposed

## Context
5M parents; SMS must not slow the post request.

## Decision
Post writes one job to a queue; 50 workers send in batches.

## Consequences
- post answers in ~50 ms
- SMS may arrive minutes later
- workers must be idempotent

The tests include тЬЕ L16 the bill adds up and the ADR has its sections.

ЁЯПБ What you just proved

The first bill is $1,216 a month, and the biggest line is egress ($450), not the servers. Doubling the servers adds $360. Five years of storage (11,000 GB тАФ lesson 02's ~11 TB) adds only $207. But letting parents download photos (10├Ч egress) makes the bill $5,266 тАФ one product idea changed the cost more than every server decision together. That is the reason for a cost line in every design. And an ADR is short: status, context, decision, and consequences that include the bad ones ("SMS may arrive minutes later").

тЪая╕П Common mistakes

ЁЯПн In production

On a real account тАФ see what the bill really is, per service, for last month:

aws ce get-cost-and-usage --time-period Start=2026-08-01,End=2026-09-01 \
    --granularity MONTHLY --metrics UnblendedCost \
    --group-by Type=DIMENSION,Key=SERVICE

A monthly budget with an email at 80% of the forecast:

aws budgets create-budget --account-id 111122223333 \
    --budget '{"BudgetName":"notice-board","BudgetLimit":{"Amount":"1500","Unit":"USD"},"TimeUnit":"MONTHLY","BudgetType":"COST"}' \
    --notifications-with-subscribers '[{"Notification":{"NotificationType":"FORECASTED","ComparisonOperator":"GREATER_THAN","Threshold":80},"Subscribers":[{"SubscriptionType":"EMAIL","Address":"planning-office@school.example"}]}]'

Tag every resource with the part of the design it belongs to, so the bill can be split the same way as the diagram (activate the tag as a cost allocation tag):

aws resourcegroupstaggingapi tag-resources \
    --resource-arn-list arn:aws:rds:ap-south-1:111122223333:db:notices-db \
    --tags component=notices-db,app=notice-board

ADRs as files in the repo, numbered:

docs/adr/
  0001-cache-the-class-feed-in-redis.md
  0002-send-urgent-notices-by-sms-through-a-queue.md
  0003-postgresql-as-the-main-store.md

ЁЯПн Why this matters in production: review the real bill against the plan's bill every month for the first quarter. A line that is twice the estimate is a design question тАФ and the answer is often an ADR.

тПня╕П Next

The method is complete: requirements, numbers, API, data, blocks, failure, security, observability, cost, ADRs. Now use it end to end. Worked designs: URL shortener, notifications and chat.

git checkout lesson-17-designs-short-notify-chat
тЖР PreviousobservabilityNext тЖТdesigns short notify chat

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