System Design (High-Level Design) Module 2 – Back-of-the-envelope estimation
Estimation drills: five systems end to end
Five worked estimation drills, from a link shortener to real-time payments: traffic, storage, bandwidth, cache and servers, and the decision each number drives.
What you will learn
- Complete an estimate sheet for a system in about ten minutes, from assumptions to server counts
- Justify each assumption and say how changing it would change the design
- Recognise when an estimate changes the architecture, such as when writes outgrow one database primary
Before you start
On this page
The previous lessons each taught one estimate. A design round, or a real design review, needs all of them at once, quickly, and in an order that ends in a decision. This lesson works through five systems the same way, from assumptions to server counts, so that the method becomes a habit. Read each drill’s requirements, try the arithmetic yourself before you look at the output, and then compare your numbers and, more importantly, your conclusions.
The order in which a drill fills its estimate sheet
Text description of the diagram
The diagram is a chain of seven boxes from top to bottom.
- Requirements and assumptions, stated out loud.
- Traffic: the average and the peak, split into reads and writes.
- Storage: per day, over the retention period, with the copies kept.
- Bandwidth: ingress and egress at the peak.
- Memory: the hot set a cache must hold for a hit-ratio target.
- Servers and partitions, at a target utilisation and with spares.
- The decision: one sentence that the numbers support.
The goal is never the precision of the numbers. An estimate that is off by 30% but leads to the right decision (this needs partitioning; this fits on one primary; egress will dominate the cost) has done its job. An estimate that is exact to five digits and leads nowhere has not.
How the drills are built
Each drill is a short script that states its assumptions as named constants at the top, so you can see, question and change every one of them. They share a few helpers for units and for rounding to two significant figures:
# Helpers shared by the five drills: units, rounding to two significant figures, and the sheet's layout.
import math
import textwrap
DAY = 86_400
YEAR = 365 * DAY
def sig(x, digits=2):
"""x rounded to `digits` significant figures (estimates carry no more)."""
if x == 0:
return 0
value = round(x, digits - 1 - math.floor(math.log10(abs(x))))
return int(value) if abs(value) >= 10 ** (digits - 1) else value
def rate(per_second):
return f"{sig(per_second):,}/s"
def size(n_bytes):
for unit, factor in (("PB", 10**15), ("TB", 10**12), ("GB", 10**9), ("MB", 10**6), ("KB", 10**3)):
if n_bytes >= factor:
return f"{sig(n_bytes / factor):,} {unit}"
return f"{sig(n_bytes):,} B"
def bits(bytes_per_second):
bps = bytes_per_second * 8
for unit, factor in (("Tbit/s", 10**12), ("Gbit/s", 10**9), ("Mbit/s", 10**6)):
if bps >= factor:
return f"{sig(bps / factor):,} {unit}"
return f"{sig(bps / 10**3):,} kbit/s"
def servers(peak_rps, per_server_rps, target=0.6, spares=2):
"""Servers for the peak at the target utilisation, plus spares for failures and deploys."""
return math.ceil(peak_rps / (per_server_rps * target)) + spares
def line(label, text):
"""One row of the sheet, wrapped at 78 characters."""
print(textwrap.fill(text, width=78, initial_indent=f" {label:<10}", subsequent_indent=" " * 12)) The drills also need a few capacities that only a load test of real software can give: how many durable writes a second one database primary sustains, how many open connections one server holds, how many requests one app server serves. The scripts use round placeholders (5,000 writes a second for a primary, for example) and say so. In a real design, measure them, and when you cannot, say out loud that they are assumptions.
Drill 1: a link shortener
Requirements: anyone can create a short link to a long URL; opening the short link redirects to the long one; links never expire. Assume 100 million new links a month, and that each link is opened 100 times over its life.
# Drill 1: a link shortener. Create short links; redirect anyone who opens one; links never expire.
import math
from sheet import DAY, bits, line, rate, servers, size
NEW_LINKS_PER_MONTH = 100_000_000
CLICKS_PER_LINK = 100 # redirects per link created (reads : writes = 100 : 1)
PEAK = 3 # busiest hour against the average
RECORD = 500 # bytes per link: code, long URL, owner, times, index entries
COPIES = 3
YEARS = 5
REDIRECT = 400 # bytes of a redirect response
HOT_SHARE = 0.2 # share of a year's links that receive most clicks
ENTRY = 600 # bytes per cached link, with the cache's overhead
APP_RPS = 2_000 # redirects one server sustains (from a load test)
writes = NEW_LINKS_PER_MONTH / (30 * DAY)
reads = writes * CLICKS_PER_LINK
links = NEW_LINKS_PER_MONTH * 12 * YEARS
print("Link shortener")
line("Traffic", f"new links {rate(writes)} on average, {rate(writes * PEAK)} at peak; redirects {rate(reads * PEAK)} at peak")
line("Storage", f"{links / 10**9:.0f} billion links in {YEARS} years x {RECORD} B x {COPIES} copies = {size(links * RECORD * COPIES)}")
line("Bandwidth", f"{bits(reads * PEAK * REDIRECT)} of redirects out at peak")
line("Cache", f"the hottest {HOT_SHARE:.0%} of a year's links: {size(NEW_LINKS_PER_MONTH * 12 * HOT_SHARE * ENTRY)}")
line("Servers", f"{servers(reads * PEAK, APP_RPS)} app servers at 60% of {APP_RPS:,} redirects/s each, plus 2 spares")
line("Decision", f"reads are {CLICKS_PER_LINK} times writes, so a cache in front of the database serves most redirects, while {rate(writes * PEAK)} of writes fit one primary. Codes of 7 characters from 62 letters and digits give {62**7 / links:,.0f} times the codes needed; {math.ceil(math.log(links, 62))} would be the bare minimum.") Output
Link shortener
Traffic new links 39/s on average, 120/s at peak; redirects 12,000/s at
peak
Storage 6 billion links in 5 years x 500 B x 3 copies = 9.0 TB
Bandwidth 37 Mbit/s of redirects out at peak
Cache the hottest 20% of a year's links: 140 GB
Servers 12 app servers at 60% of 2,000 redirects/s each, plus 2 spares
Decision reads are 100 times writes, so a cache in front of the database
serves most redirects, while 120/s of writes fit one primary.
Codes of 7 characters from 62 letters and digits give 587 times
the codes needed; 6 would be the bare minimum.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 shortener.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
What the numbers decide: this is a read-heavy service with tiny responses. A hundred redirects per link means a cache in front of the database can serve most of the traffic, and a modest number of app servers handles the peak. Writes are trivial for one primary, and nine terabytes over five years, with copies, fits a small database cluster. The code length is settled by arithmetic, not taste: seven characters from 62 symbols leave hundreds of times the codes needed.
Drill 2: chat
Requirements: one-to-one and small-group messages, delivered at once to recipients who are online, with a year of history kept on the servers. Assume 50 million daily users who each send 40 messages and receive or load 80 a day.
# Drill 2: one-to-one and small-group chat. Send messages, deliver them to online recipients at once, keep history.
import math
from sheet import DAY, bits, line, rate, servers, size
DAU = 50_000_000
SENT = 40 # messages each user sends a day
DELIVERED = 80 # messages each user receives or loads a day
PEAK = 3
MESSAGE = 200 # bytes stored per message: text, ids, times, index entries
COPIES = 3
KEEP_DAYS = 365 # history kept on the servers
ONLINE_AT_PEAK = 0.25 # share of daily users connected at the busiest moment
PER_CONNECTION_SERVER = 100_000 # open connections one server holds (from a load test)
PRIMARY_WRITES = 5_000 # durable writes one database primary sustains (an assumption to measure)
writes = DAU * SENT / DAY
reads = DAU * DELIVERED / DAY
connections = DAU * ONLINE_AT_PEAK
partitions = math.ceil(writes * PEAK / PRIMARY_WRITES)
print("Chat")
line("Traffic", f"messages sent {rate(writes)} on average, {rate(writes * PEAK)} at peak; deliveries {rate(reads * PEAK)} at peak")
line("Storage", f"{size(DAU * SENT * MESSAGE * COPIES)} a day with copies, {size(DAU * SENT * MESSAGE * COPIES * KEEP_DAYS)} for a year of history")
line("Bandwidth", f"{bits(reads * PEAK * MESSAGE)} of messages out at peak")
line("Servers", f"{connections / 10**6:.1f} million open connections need {servers(connections, PER_CONNECTION_SERVER)} connection servers (60% of {PER_CONNECTION_SERVER:,} each, plus 2 spares); message storage needs {partitions} write partitions")
line("Decision", f"{rate(writes * PEAK)} of message writes at peak is {writes * PEAK / PRIMARY_WRITES:.0f} times what one primary takes, so partition the messages by conversation; the open connections, not the bytes, set the size of the delivery fleet.") Output
Chat
Traffic messages sent 23,000/s on average, 69,000/s at peak; deliveries
140,000/s at peak
Storage 1.2 TB a day with copies, 440 TB for a year of history
Bandwidth 220 Mbit/s of messages out at peak
Servers 12.5 million open connections need 211 connection servers (60% of
100,000 each, plus 2 spares); message storage needs 14 write
partitions
Decision 69,000/s of message writes at peak is 14 times what one primary
takes, so partition the messages by conversation; the open
connections, not the bytes, set the size of the delivery fleet.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 chat.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
What the numbers decide: the message bytes are small (a few hundred megabits a second at the peak), but the write rate is not. Tens of thousands of message writes a second is many times what one primary takes, so messages are partitioned, and partitioning by conversation keeps the messages of one chat together. The size of the delivery fleet is set by something else again: millions of open connections, one per online user, counted with the share of users online at the busiest moment.
Drill 3: a video platform
Requirements: people upload videos, which are transcoded into several sizes and streamed to viewers. Assume 50 million daily viewers who watch an hour each at an average of 3 Mbit/s, and 100,000 uploads a day of 500 MB.
# Drill 3: a video platform. Upload videos, transcode them into several sizes, stream them to viewers.
from sheet import DAY, bits, line, size
DAU = 50_000_000
WATCH_SECONDS = 60 * 60 # an hour of viewing per user a day
BITRATE = 3 * 10**6 / 8 # bytes per second of an average stream (3 Mbit/s)
PEAK = 2
UPLOADS_PER_DAY = 100_000
ORIGINAL = 500 * 10**6 # bytes of an average uploaded video
RENDITIONS = 1.5 # transcoded copies at several sizes, as a multiple of the original
COPIES = 3
CDN_HIT = 0.95 # share of bytes served from caches near viewers
egress = DAU * WATCH_SECONDS * BITRATE / DAY
ingress = UPLOADS_PER_DAY * ORIGINAL / DAY
stored_per_day = UPLOADS_PER_DAY * ORIGINAL * (1 + RENDITIONS) * COPIES
print("Video platform")
line("Traffic", f"{DAU * WATCH_SECONDS / DAY * PEAK / 10**6:.1f} million streams playing at the busiest moment")
line("Storage", f"{size(stored_per_day)} a day with renditions and copies, {size(stored_per_day * 365)} a year")
line("Bandwidth", f"in {bits(ingress * PEAK)} at peak; out {bits(egress * PEAK)} at peak, of which the origin serves {bits(egress * PEAK * (1 - CDN_HIT))}")
line("Decision", f"egress is {egress / ingress:,.0f} times ingress and dwarfs everything else: stream from a CDN, and size the origin for its cache misses, not for the viewers.") Output
Video platform
Traffic 4.2 million streams playing at the busiest moment
Storage 380 TB a day with renditions and copies, 140 PB a year
Bandwidth in 9.3 Gbit/s at peak; out 12 Tbit/s at peak, of which the origin
serves 630 Gbit/s
Decision egress is 1,350 times ingress and dwarfs everything else: stream
from a CDN, and size the origin for its cache misses, not for the
viewers.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 video.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
The concurrent streams come from Little’s law: viewing sessions start at some rate and last an hour each, and the rate times the duration is the number playing at any moment. What the numbers decide: egress is more than a thousand times ingress and runs to terabits a second, which no origin should serve by itself. Video is streamed from a content delivery network, and the origin is sized for the share the CDN does not serve from its caches, here 5%. Storage is the second large figure: about 140 PB a year with renditions and three copies. At that size, erasure coding, with half the raw space of three replicas, is worth its extra CPU and network for the videos that are rarely watched (HDFS erasure coding).
Drill 4: a news feed
Requirements: people post, and each person’s feed shows the newest posts of the accounts they follow. Assume 20 million daily users who load the feed 50 times a day, post once every two days, and have 200 followers on average.
# Drill 4: a news feed. People post; each follower's feed shows the newest posts of the accounts they follow.
from sheet import DAY, bits, line, rate, size
DAU = 20_000_000
FEED_LOADS = 50 # feed reads per user a day
POSTS = 0.5 # posts per user a day
FOLLOWERS = 200 # average followers of an account that posts
PEAK = 2.5
FEED_PAGE = 20 * 1_000 # bytes of a feed page: 20 items of about 1 KB
TIMELINE_ENTRY = 16 # bytes to store "post id, time" in one follower's timeline
KEEP_PER_USER = 500 # timeline entries kept for each user
COPIES = 3
posts = DAU * POSTS / DAY
inserts = posts * FOLLOWERS # fan-out on write: one insert per follower
reads = DAU * FEED_LOADS / DAY
print("News feed")
line("Traffic", f"posts {rate(posts * PEAK)} at peak; feed loads {rate(reads * PEAK)} at peak")
line("Fan-out", f"each post goes to {FOLLOWERS} timelines: {rate(inserts)} inserts on average, {rate(inserts * PEAK)} at peak")
line("Storage", f"timelines of {KEEP_PER_USER} entries for {DAU / 10**6:.0f} million users: {size(DAU * KEEP_PER_USER * TIMELINE_ENTRY * COPIES)} with copies")
line("Bandwidth", f"{bits(reads * PEAK * FEED_PAGE)} of feed pages out at peak")
line("Decision", f"writing each post into every follower's timeline multiplies {rate(posts * PEAK)} of posts into {rate(inserts * PEAK)} of inserts, but makes each feed load one cheap read; accounts with millions of followers are better merged in when the feed is read.") Output
News feed
Traffic posts 290/s at peak; feed loads 29,000/s at peak
Fan-out each post goes to 200 timelines: 23,000/s inserts on average,
58,000/s at peak
Storage timelines of 500 entries for 20 million users: 480 GB with copies
Bandwidth 4.6 Gbit/s of feed pages out at peak
Decision writing each post into every follower's timeline multiplies 290/s
of posts into 58,000/s of inserts, but makes each feed load one
cheap read; accounts with millions of followers are better merged
in when the feed is read.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 feed.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
What the numbers decide: the interesting figure is neither the posts nor the reads, but the fan-out. Writing every post into each follower’s timeline turns a few hundred posts a second into tens of thousands of inserts a second, and in exchange each feed load becomes one cheap read of a precomputed list. That trade pays while audiences are small. An account with millions of followers would turn one post into millions of inserts, so such accounts are better merged into the feed when it is read.
Drill 5: real-time payments
Requirements: people pay each other from bank account to bank account, and every payment must be recorded exactly once and durably. Assume 30 million daily users who make 2 payments and check their status or history 4 times a day, with a sharp peak factor of 4.
A drill modelled on UPI-style payments
This drill is modelled on account-to-account payments of the kind UPI provides in India, but its numbers are illustrative assumptions, not UPI’s. The traffic lesson compared the same estimate with the Reserve Bank of India’s Payment System Indicators: about 690 payments a second on average would be about 7.6% of a month in which the whole network handled 2,45,089.58 lakh payments.
# Drill 5: real-time payments between bank accounts. Every payment must be recorded exactly once, durably.
import math
from sheet import DAY, line, rate, sig, size
DAU = 30_000_000
PAYMENTS = 2 # payments per user a day
STATUS_READS = 4 # status and history reads per user a day
PEAK = 4
WRITES_PER_PAYMENT = 4 # the payment, a debit and a credit entry, and an event for other systems
RECORD = 1_500 # bytes per payment across those rows, with index entries
COPIES = 3
KEEP_YEARS = 10
PRIMARY_WRITES = 5_000 # durable writes one database primary sustains (an assumption to measure)
payments = DAU * PAYMENTS / DAY
writes = payments * WRITES_PER_PAYMENT
reads = DAU * STATUS_READS / DAY
print("Real-time payments")
line("Traffic", f"payments {rate(payments)} on average, {rate(payments * PEAK)} at peak; status reads {rate(reads * PEAK)} at peak")
line("Writes", f"{WRITES_PER_PAYMENT} durable writes per payment: {rate(writes * PEAK)} at peak")
line("Storage", f"{size(DAU * PAYMENTS * RECORD * COPIES * 365)} a year with copies, {size(DAU * PAYMENTS * RECORD * COPIES * 365 * KEEP_YEARS)} over {KEEP_YEARS} years")
line("Decision", f"about {sig(writes * PEAK):,} durable writes a second at peak, so one primary database will not be enough: partition the ledger into at least {math.ceil(writes * PEAK / PRIMARY_WRITES)} partitions by payment id, so that each payment's {WRITES_PER_PAYMENT} writes stay inside one of them.") Output
Real-time payments
Traffic payments 690/s on average, 2,800/s at peak; status reads 5,600/s
at peak
Writes 4 durable writes per payment: 11,000/s at peak
Storage 99 TB a year with copies, 990 TB over 10 years
Decision about 11,000 durable writes a second at peak, so one primary
database will not be enough: partition the ledger into at least 3
partitions by payment id, so that each payment's 4 writes stay
inside one of them.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 payments.py
Runs on this device, in your browser. The first run downloads Python (about 13.5 MB), which is kept for the next runs.
Your run, in this browser
What the numbers decide: a payment is several durable writes, not one, and the peak factor is high. About 11,000 durable writes a second at the peak is more than one primary database will take, so the ledger is partitioned. The partition key decides how hard each payment is. Keyed by payment id, all four writes of a payment land in one partition, so each payment stays one local transaction; an account’s history then needs an index that spans the partitions. Keyed by account, the payer’s and the payee’s entries usually sit in different partitions, so most payments must be coordinated across two of them, either with a distributed transaction, which adds latency and new ways to fail, or as separate local transactions that leave the two accounts briefly inconsistent (Sharding pattern).
Durable writes are also where the latency lesson’s measurements matter: each commit waits for a flush to the drive, and databases raise their throughput with group commit, which makes several transactions durable with one flush (PostgreSQL commit_delay). The storage, under a petabyte for ten years with copies, is the easy part.
Say the result in a sentence
At the end of an estimate, say what it means in one sentence that names the number and the decision: “about 11,000 writes a second at the peak, so one primary database will not be enough”, or “egress is over a thousand times ingress, so we stream from a CDN”. The sentence is what the people in the room remember, and it invites the right challenge: someone can now ask whether the peak factor of 4 is right, which is a better conversation than one about arithmetic.
When an estimate changes the architecture
Most estimates confirm a simple design. Look for the few that change it:
- Writes outgrow one primary. Then partition the data, and choose the key so that the operations that must be atomic stay in one partition: one server’s disks, processors and network links all run out at some size (Sharding pattern).
- Data outgrows one machine. Petabytes of objects go to storage built for them, and the copies decide the raw size.
- Egress outgrows the origin. Then caches close to users carry the bytes, and the origin serves their misses.
- The hot set outgrows one cache node. Then the cache itself is partitioned, or the hit-ratio target is lowered on purpose.
- Connections outgrow one server. Then a fleet of connection servers sits in front of everything else, sized by the users online at the peak.
Key takeaways
- Fill the sheet in order: assumptions, traffic, storage, bandwidth, memory, servers and partitions, decision.
- State every assumption as a named number, including the capacities that only a load test can give.
- Look for the figure that dominates: redirects for a shortener, connections and writes for chat, egress for video, fan-out for a feed, durable writes for payments.
- Finish with one sentence that names the number and the decision it drives.
Exercise
Exercise · Medium · Python
Fill in the estimate sheet of a paste service
A paste service stores text that people paste and share by link. Write paste_sheet(...) in paste.py so that it returns the six figures of the service's estimate sheet as a dictionary. Its parameters are the assumptions, with the values of the first sample test in brackets:
new_per_day: pastes created a day (1,000,000)reads_per_paste: times each paste is read over its life (10)paste_bytes: the average size of a paste (10,000)keep_days: days a paste is kept (365)copies: copies kept of every paste (3)peak: the peak factor against the daily average (3)per_server_rps: requests a second one app server sustains (1,000)target: the highest utilisation allowed per server (0.6)spares: spare servers for failures and deploys (2)hot_share: the share of the kept pastes a cache should hold (0.1)entry_overhead: extra bytes per cached paste (100)
Return these keys:
"peak_writes"and"peak_reads": requests a second at the peak, where reads a day are the pastes created a day times the reads of each paste;"stored_bytes": every paste kept forkeep_daysdays, with its copies;"peak_egress_gbit": the peak reads times the paste size, in gigabits a second;"cache_bytes": thehot_shareof the kept pastes, each with its overhead;"app_servers": servers for the peak reads and writes together at the target utilisation, rounded up, plus the spares.
The sample tests accept each figure within 10%, as an estimate should, except the server count, which must be exact.
Starter code · paste.py
import math
DAY = 86_400
def paste_sheet(new_per_day, reads_per_paste, paste_bytes, keep_days, copies, peak,
per_server_rps, target, spares, hot_share, entry_overhead):
"""The estimate sheet of a paste service, as a dictionary of six figures."""
# Replace this line with your code.
return {} The sample tests · test_paste.py
import math
from paste import paste_sheet
FIRST = dict(new_per_day=1_000_000, reads_per_paste=10, paste_bytes=10_000, keep_days=365, copies=3, peak=3,
per_server_rps=1_000, target=0.6, spares=2, hot_share=0.1, entry_overhead=100)
SECOND = dict(new_per_day=20_000_000, reads_per_paste=50, paste_bytes=4_000, keep_days=30, copies=2, peak=4,
per_server_rps=2_000, target=0.5, spares=3, hot_share=0.2, entry_overhead=200)
def near(actual, expected):
"""Within 10%, as an estimate should be."""
return math.isclose(actual, expected, rel_tol=0.10)
def test_rates():
"""computes peak writes and reads a second"""
sheet = paste_sheet(**FIRST)
assert near(sheet["peak_writes"], 34.7)
assert near(sheet["peak_reads"], 347)
assert near(paste_sheet(**SECOND)["peak_reads"], 46_300)
def test_storage():
"""keeps every paste with its copies"""
assert near(paste_sheet(**FIRST)["stored_bytes"], 1.095e13)
assert near(paste_sheet(**SECOND)["stored_bytes"], 4.8e12)
def test_egress():
"""turns peak reads into gigabits a second"""
assert near(paste_sheet(**FIRST)["peak_egress_gbit"], 0.0278)
assert near(paste_sheet(**SECOND)["peak_egress_gbit"], 1.48)
def test_cache():
"""sizes the cache for the hot share of the kept pastes, overhead included"""
assert near(paste_sheet(**FIRST)["cache_bytes"], 3.69e11)
assert near(paste_sheet(**SECOND)["cache_bytes"], 5.04e11)
tiny_pastes = dict(FIRST, paste_bytes=200, entry_overhead=300)
assert near(paste_sheet(**tiny_pastes)["cache_bytes"], 1.825e10)
def test_servers():
"""rounds the servers up and adds the spares"""
assert paste_sheet(**FIRST)["app_servers"] == 3
assert paste_sheet(**SECOND)["app_servers"] == 51 A hint
Work in the order of the sheet: rates first (new_per_day / DAY * peak), then storage (new_per_day * keep_days * paste_bytes * copies), then bandwidth from the peak reads (bytes a second times 8, divided by 10**9), then the cache, then math.ceil(total_peak / (per_server_rps * target)) + spares.
Results of the sample tests
| Test | Result | Details |
|---|
What your code printed
The sample tests run on this device, in your browser (Pyodide): nothing is sent to mysmartcopilot.com. The first run downloads Python (about 13.5 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.
Check yourself
5 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.
References
- Prefixes for binary multiples (National Institute of Standards and Technology (NIST))
- textwrap, text wrapping and filling (Python Software Foundation)
- HDFS Erasure Coding (Apache Hadoop documentation) (The Apache Software Foundation)
- PostgreSQL documentation: Write Ahead Log settings (commit_delay) (The PostgreSQL Global Development Group)
- Sharding pattern (Azure Architecture Center) (Microsoft)
- Payment System Indicators (Reserve Bank of India)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress