ET-1 — Revision session (27 Aug 2026)¶
This session walked through the January 2026 AN paper question by question. Nearly every concept below has a solved PYQ with full reasoning — this page records what the professor said about it, in enough detail that you're hearing him explain it.
The exam format (announced this session)¶
Straight from the professor
- The paper will have MCQs, MSQs, and numerical problems — plus subjective questions, newly introduced. "A staple at the degree level to test depth."
- Practice PYQs on the portal — but "don't take a very old paper, which might not be very relevant." The recent-era papers are the guide.
- If you see a 500 error in a scenario, the debugging focus is the backend code and database layers — not the frontend.
A student asked him to explain the concept behind each question, not just the answer. He agreed — and spent the session doing exactly that. The message: the exam tests whether you understand why a system behaves the way it does, not whether you've memorized the answer.
CORS — the security rationale, not just the definition¶
The scenario he walked through: a frontend application running on localhost:3000 tries to fetch data from a backend API on localhost:8000. The browser throws a CORS error.
First, what "origin" means: an origin is scheme + domain + port. Even though both applications are on the same machine (localhost), the different port numbers make them different origins. The browser treats them as strangers.
His reasoning for why the policy exists — worth remembering word-for-word:
Modern web applications rely on heavily nested third-party JavaScript libraries. If a library is compromised, it could execute unauthorized requests to an external server and exfiltrate sensitive client data. The Same-Origin Policy acts as a guardrail to stop this exfiltration.
Picture the attack: you're logged into your bank in one tab (your browser holds your session). You open evil.com in another tab. Evil.com's JavaScript tries fetch("https://bank.com/api/me") — the browser blocks the read, because bank.com never said evil.com could listen. Without the policy, a single compromised npm package on any website could silently read your data from every other site you're logged into.
The fix: the destination server must explicitly configure its headers to allow the calling origin. In FastAPI, that's the CORS middleware with allow_origins=["http://localhost:3000"].
He also clarified what CORS is not: it's not a server error. curl, Postman, and Python scripts are never blocked by CORS — only browser JavaScript is. Your API may even be working perfectly when the browser shows a CORS error; it's the browser's read that's refused.
Asked in 6 places: T1-AN Q13 — the fix · T1-AN Q18 — the error side · T1-FN Q2 — the reason · T1-FN Q18 — the local HTML block · Week 2 — CORS section · GA2
GitHub Pages — static only, and why the form fails¶
The scenario: a student deploys a portfolio website to GitHub Pages and tries to add a Python FastAPI backend to handle form submissions directly on the platform.
His explanation: GitHub Pages is exclusively designed to serve static files — HTML, CSS, images, and client-side JavaScript assets. There is no backend runtime execution environment, no database storage, no Python process. The form submission simply has nothing to receive it — it's like mailing a letter to an address that has no mailbox.
The correct architecture: static frontend on Pages, backend on a real runtime (a Cloud Function, a container, a server). This exact scenario became a short-answer question in the FN shift (T1-FN Q3).
JSON parsing — the single-quote crash (demonstrated live)¶
He showed this code on screen:
import json
# This throws json.decoder.JSONDecodeError because of single quotes
try:
json.loads("{'score': 10}")
except json.decoder.JSONDecodeError as e:
print(f"Error: {e}")
# Correct version using double quotes inside the string
valid_json = json.loads('{"score": 10}')
print(valid_json['score']) # Output: 10
The rule: standard JSON requires double quotes for keys and string values. Python dictionary syntax ({'score': 10}) uses single quotes and is not JSON. The parser is strict — even if a string looks like a valid Python dict, json.loads rejects it if it breaks the double-quote specification.
This pairs with the response.text vs response.json() distinction he also covered: .text gives you the raw string, .json() parses it into a dictionary you can index. After .json() you can write data["score"]; after .text you can't.
Asked in: T1-AN Q7 — .text vs .json() · T1-FN Q4 — response.json() · T1-FN Q20 — the malformed JSON crash
Docker — environment parity + the missing install¶
Scenario 1 (parity): you build a Docker image locally on Linux and push it to Docker Hub. Your colleague pulls it on Windows. The code runs Ubuntu Linux inside the container. What happens?
His explanation: Docker's core design is to package and configure the entire runtime environment starting from the operating system level. Because the OS and dependencies are completely encapsulated inside the image, it runs successfully on Windows (or any platform) with exact parity. The "works on my machine" problem dies here — every machine running the container is the same machine.
Scenario 2 (the crash): a Python web app container crashes immediately with "cannot find the requests library." What's missing?
His explanation: Docker containers are completely isolated. You must explicitly write commands in the Dockerfile to install every single dependency required by your script. The crash occurs because the package installation step was omitted:
He paired these two ideas deliberately: parity works because isolation is total. The same wall that keeps your laptop's environment out is what makes the container identical everywhere.
Asked in: T1-FN Q5 — parity · T1-FN Q11 — the missing install · T3-FN Q30 — the install line · Week 2 — Docker section
HTTPS — the Caesar cipher explanation¶
A student (Disha Rani) asked what "encrypts data" actually means. His answer, in full:
Encryption is a cybersecurity concept where you run plaintext data through a mathematical algorithm to scramble it into randomized text that nobody can understand during transmission. He explained it with the Caesar cipher (a Roman-era textbook example):
- Set the shift key to n = 2
- Every letter shifts forward by 2: 'a' → 'c', 'h' → 'j', 'e' → 'g'
- Instead of transmitting "Hello World" in cleartext, the sender transmits the shifted ciphertext
- The receiver applies the reverse shift (subtract 2) to recover the plaintext
His practical warning: the Caesar cipher is absolutely not used in modern web protocols — but it's the perfect mental model for what encryption does. HTTPS does the same thing (scramble in transit, unscramble at the ends) with industrial-strength mathematics.
Why it matters for API tokens: if you send a token over plain HTTP, it travels in cleartext — anyone intercepting the network traffic reads it. HTTPS encrypts the payload so only the sender and receiver can decode it.
Asked in: T1-FN Q6 — why HTTPS for tokens · Week 2 — deployment
asyncio.gather — the bottleneck arithmetic¶
The scenario: you trigger 5 network requests in parallel using asyncio.gather. Four complete in exactly 1 second; one takes 10 seconds.
His explanation, step by step:
asyncio.gatherruns all provided functions concurrently — all 5 launch simultaneously- Four finish after 1 second, but gather cannot yield the final combined response until every registered task completes
- It must block and wait for the slowest task
- Total execution time: 10 seconds
The key insight: concurrency removes the sum (which would be 14 seconds sequentially: 4×1 + 10), not the max. Parallelism does not fix a single slow dependency — the hanger holds everything hostage. In the scenario version (T1-AN Q21), one node hangs for 60 seconds and the entire server process halts — which is why production code wraps each call in asyncio.wait_for(..., timeout=...).
Secret keys — the .env workflow and the .gitignore oversight¶
His exact workflow:
- Never hardcode or type private API/secret keys directly into Python files
- Store them in a local
.envfile - Immediately add
.envto.gitignore— the oversight he named specifically: "developers often remember to use .env files but forget to list them in .gitignore" - Load in code with
os.getenv("API_KEY")
The risk, in his words: if you forget the .gitignore and push to GitHub, your .env file with secret keys goes to the public web. Rogue actors can scrape your repository, copy your keys, and impersonate you — resulting in a security disaster: data breaches, financial loss.
The key point about git history: once a secret is pushed, it's in the history forever. Deleting the file in a later commit doesn't remove it from earlier commits — anyone who clones the repo has the full history including the secret. This is why the workflow must be right from the first commit.
Asked in 5+ places: T1-AN Q5 — the .gitignore MSQ · T1-FN Q8 — why .env exists · T3-AN Q24 — where the key lives · T3-FN Q6 — environment variables · Week 2 — secrets section
Serverless memory limits — the 2GB file story¶
The scenario: a script loads a 2GB file into memory. It works perfectly on your laptop but repeatedly crashes with "Out of Memory" on a free-tier Cloud Function.
His explanation: serverless Cloud Functions run in lightweight virtual environments that enforce very strict RAM limits — typically 128–512 MB on free tiers. Trying to read a massive file in one go exceeds the allocation, and the operating system immediately terminates the process.
The numbers to remember: your laptop has 8–32 GB of RAM; a free-tier function has 128–512 MB. Local success proves nothing about a constrained environment.
The fix: optimize data loading — streaming, chunking, reading only what you need — instead of loading everything into memory at once. Or move the job to a container where you control the resources.
This is the RAM twin of the timeout story (T1-AN Q17): serverless gives you strict memory and strict time. Together they're the platform's fundamental trade-off — you get zero-ops deployment, but you live within hard ceilings.
Prompt injection filters — case variation defeats matching¶
The scenario: an application filters out the word "ignore" to prevent prompt injections. A user types "IGNORE" in capital letters and successfully bypasses the filter.
His explanation: relying on exact string-matching filters is incredibly weak. Users can bypass them with basic variations in casing, spelling, or formatting:
"ignore"→ blocked"IGNORE"→ passes (different case)"I-g-n-o-r-e"→ passes (hyphenated)"1gn0re"→ passes (leetspeak)
The robust solution he gave: convert the incoming user text to lowercase before evaluation, then use Regular Expressions (Regex) to flag pattern matches rather than checking exact words. The regex catches ignore, IGNORE, Ign0re and variants — because it matches the pattern, not the string.
He connected this to the deeper defense in the next topic: role separation (system vs user prompts) is the structural protection; filtering is one layer on top.
Caching — the Twitter slow-network example¶
A student (Manoj Kumar Khatri) said: "I can't understand caching." His answer, in full:
The example: load Twitter on a slow internet connection. You'll notice the sidebar templates, static images, and structural UI elements render instantly — no lag — even though the actual tweets take a long time to load. This is because Twitter caches those static assets directly inside your local browser's storage. Your browser loads them from disk instead of wasting bandwidth fetching them over the slow network.
The header: Cache-Control: public, max-age=3600 does two things:
publicauthorizes intermediate proxies and CDNs to serve cached copies directlymax-age=3600means the copy is fresh for 3600 seconds (1 hour)
The benefit: subsequent identical requests never reach your origin backend server — saving massive amounts of network bandwidth and compute.
The cache-buster: what if you update your profile picture but the browser cached the old one for 2 hours? The client's JavaScript can append a random query parameter (?t=1 or ?time=123) to the URL. Because the URL changed, it mutates the cache key — the browser/CDN sees it as a completely new request and must fetch fresh from the server.
Asked in 8+ places: T1-AN Q19 — what max-age does · T1-AN Q22 — the cache-buster · T1-FN Q13 + Q19 + Q21 · T3-FN Q9 — the dashboard TTL · Week 2 — caching section
LLM prompts — system vs user hierarchy¶
His explanation: developers split inputs into two roles:
- System prompt (or developer prompt): declares core operational rules — "you are a strict SQL compiler assistant," "always respond in JSON," safety constraints
- User prompt: carries the transient, user-supplied input — the actual question
Large Language Models are mathematically tuned to assign significantly higher attention weights to system prompts. This hierarchy ensures the model adheres to its core operational rules and safety constraints even if a user tries to override them in their prompt. Separating the inputs prevents users from easily tricking the model — the LLM obeys system instructions much more diligently than user instructions.
This is the structural defense behind the injection-filter question above: role separation is the wall; filtering is a screen door on top of it.
Asked in: T1-AN Q23 — defending the LLM · T1-FN Q15 — why the separation works · Week 3 — prompt engineering
Database race conditions — the simultaneous signup¶
The scenario: two users attempt to sign up with the exact same username at the same millisecond. Your server checks if the username exists, then adds it if available — without any locking.
His explanation, step by step:
- Both requests arrive simultaneously
- The server checks availability for both at the same time — before either has committed a write
- Both checks return "available" (neither write has happened yet)
- Both processes attempt to write to the database
- Result: duplicate user records, or a database integrity exception
This is a check-then-act race — the gap between reading a state and acting on it is where the second process sneaks in. The fix: make check-and-write atomic — a database lock, a unique constraint (the database itself rejects the second insert), or a transaction with proper isolation.
Nothing else in the stack rescues you for free — not Python, not the network, not the database on its default settings.
Asked in: T1-FN Q16 — the race condition
ML data leakage — memorization vs generalization¶
The scenario: a team discovers 200 test reviews accidentally appeared in their training data. After removing duplicates and retraining, accuracy drops from 94% to 76%.
His explanation: this is classic data leakage. When a model is exposed to test-set data during its training phase, it memorizes the correct answers for those specific examples rather than learning the generalized patterns. The 94% was the model reciting answers it had already seen — not real understanding.
Once the leaked records are removed, the model can no longer rely on memory. The 76% is the true accuracy — what the model actually learned about patterns it has never seen before.
The signature the exam rewards: suspiciously high test accuracy + shared rows between train and test = leakage until proven otherwise. The T1-AN version (Q16) adds how leakage happens in practice: shuffling a chronological dataset (temporal leakage) and computing rolling statistics before the train/test split (feature leakage).
API gateway + redundancy — the 80-microservice story¶
The scenario: a massive application serves 50 million users across 200 countries, with 80 different backend microservices — user profiles, recommendations, video encoding, billing, analytics. Managing 80 endpoints created chaos: hardcoded URLs broke during migrations, each service implemented its own authentication inconsistently.
His explanation:
The problem with hardcoded URLs: mobile developers embedded 80 different API URLs directly in the client code. When servers migrated, every URL broke — and updating a mobile app to fix URLs takes days (app store review).
What an API gateway is: a single, unified entry point. It uses centralized routing rules and path-matching logic to accept client requests and forward them to the correct backend service. It also centralizes authentication — users authenticate once at the gateway using OAuth tokens, instead of 80 services each implementing their own auth.
The single point of failure: one gateway instance means one failure away from total outage. During a traffic spike, the gateway crashes and the entire platform goes down — even though all 80 backend services are running fine. The fix: deploy multiple gateway instances behind a load balancer — if one dies, others handle the traffic.
Debugging with a gateway: a 500 error could now originate from the gateway's routing logic, the authentication middleware, the backend service, or the network between layers — you must check every layer the request crossed.
Student doubts worth recording¶
| Student asked | The professor's answer |
|---|---|
| What does "encrypts data" mean? (Disha Rani) | Plaintext → algorithm → randomized ciphertext. Explained via Caesar cipher (shift 2): 'a'→'c', 'h'→'j'. The receiver unshifts to decrypt. |
| I can't understand caching. (Manoj Kumar Khatri) | Twitter on slow network: UI renders from local cache, tweets lag. max-age=3600 authorizes CDNs to serve copies. Cache-buster: ?t=1 changes the cache key. |
| What happens with concurrent signups? (Manoj Kumar Khatri) | Without database locks, both checks pass simultaneously, both write — the race condition. Fixed by explicit locking or unique constraints. |
| Please explain the concept behind each question. (Manoj Kumar Khatri) | He agreed — and explained the underlying mechanics behind every practice question for the rest of the session. |