LLearnCodeCore lessons
⚡ OPTIONAL SKILL BOOSTERS

Small techniques that make ordinary code noticeably better.

These are optional micro-modules outside the required lesson progression. Use them when you want a practical shortcut, safer habit or next-level pattern without changing your required journey.

🐍 PythonQuick win · 8 min

List comprehensions without the mystery

Turn a common loop-plus-append pattern into compact readable Python when the transformation is simple.

prices = [799, 1499, 2499]
with_gst = [round(price * 1.18, 2) for price in prices]
TRY ITRewrite one three-line append loop from your own code as a comprehension, then decide which version is easier to scan.
💡

If the comprehension needs multiple conditions or side effects, prefer the ordinary loop.

🐍 PythonStretch · 10 min

Debug data by checking shape first

Many Python bugs come from assuming a value is a list, dict or string before inspecting it.

print(type(payload))
print(repr(payload)[:200])
print(len(payload) if hasattr(payload, "__len__") else "no length")
TRY ITAdd a temporary shape check around an input that could arrive malformed, then remove it once the assumption is verified.
💡

Inspect type, representative value and size before changing logic.

🟨 JavaScriptQuick win · 7 min

Destructure the values you actually use

Destructuring makes object-shaped data easier to read and reduces repetitive property access.

const order = { id: 42, total: 1499, status: "paid" };
const { id, total } = order;
console.log(id, total);
TRY ITTake one object with three or more fields and destructure only the fields your next calculation needs.
💡

Avoid destructuring everything by default; keep the local scope small.

🟨 JavaScriptStretch · 12 min

Run independent async work together

Sequential awaits are slower when operations do not depend on one another.

const [profile, orders] = await Promise.all([
  loadProfile(),
  loadOrders(),
]);
TRY ITFind two independent awaits in sample code and explain whether Promise.all is safe before changing them.
💡

Parallelise only independent work; preserve sequencing when one result feeds the next.

🔷 TypeScriptQuick win · 9 min

Use unknown at trust boundaries

unknown forces you to prove what external data contains before using it.

function readTotal(value: unknown) {
  if (typeof value !== "number") throw new Error("total must be a number");
  return value;
}
TRY ITReplace one imaginary any-valued API field with unknown and add the narrowest useful runtime check.
💡

Use unknown for untrusted input; narrow once, then keep the rest of the function strongly typed.

🔷 TypeScriptStretch · 10 min

Validate object shape with satisfies

satisfies checks a value against a type while preserving useful literal information.

type Plan = { name: string; seats: number };
const starter = { name: "Starter", seats: 3 } satisfies Plan;
TRY ITCreate a small configuration object and intentionally misspell one required key to see what satisfies is protecting.
💡

Use satisfies for configuration objects when you want validation without widening every property.

🗃️ SQLQuick win · 10 min

Name complicated query steps with a CTE

A CTE gives an intermediate result a name, making multi-stage analysis easier to verify.

WITH paid_orders AS (
  SELECT customer_id, amount
  FROM orders
  WHERE status = 'paid'
)
SELECT customer_id, SUM(amount) AS revenue
FROM paid_orders
GROUP BY customer_id;
TRY ITTake one query with a long nested condition and isolate the first meaningful dataset as a named CTE.
💡

A good CTE name should describe the rows it contains, not the SQL operation used to create them.

🗃️ SQLStretch · 12 min

Rank rows without collapsing them

Window functions calculate across related rows while keeping row-level detail visible.

SELECT customer_id, amount,
  ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY amount DESC) AS purchase_rank
FROM orders;
TRY ITExplain why GROUP BY cannot preserve every order row here, then change the ranking order from largest to smallest or vice versa.
💡

Think window function when you need an aggregate-like calculation but must keep individual rows.

🎨 CSSQuick win · 8 min

Fluid type with clamp()

clamp() can scale text smoothly between phone and desktop sizes without many breakpoints.

h1 {
  font-size: clamp(2.25rem, 6vw, 5rem);
  line-height: .98;
}
TRY ITChoose a sensible minimum, preferred viewport-relative size and maximum for one heading.
💡

Keep the minimum readable and the maximum visually controlled; the middle value handles the fluid range.

🎨 CSSStretch · 12 min

Think component-first with container queries

Container queries let a component adapt to the space it actually receives rather than the whole viewport.

.card-shell { container-type: inline-size; }
@container (min-width: 36rem) {
  .card { grid-template-columns: 1fr 1fr; }
}
TRY ITDescribe one reusable card whose layout should depend on its parent width rather than device width.
💡

Use media queries for page-level changes and container queries when reusable components own the decision.

📊 VBAQuick win · 7 min

Catch variable typos with Option Explicit

VBA can silently create misspelled variables unless declarations are required.

Option Explicit

Sub BuildReport()
    Dim total As Double
    total = Range("B2").Value
End Sub
TRY ITAdd Option Explicit to a small macro and declare every variable with the narrowest practical type.
💡

Turn on Require Variable Declaration in the VBA editor so new modules get Option Explicit automatically.

📊 VBAStretch · 12 min

Read ranges in bulk instead of cell by cell

Repeated worksheet reads are slow; arrays let VBA process many values in memory.

Dim data As Variant
data = Range("A2:C1000").Value

Dim i As Long
For i = 1 To UBound(data, 1)
    data(i, 3) = data(i, 1) * data(i, 2)
Next i
TRY ITIdentify where a row-by-row worksheet loop could first load a rectangular range into a Variant array.
💡

Cross the worksheet boundary fewer times; do the repeated work in memory.

🤖 PromptingQuick win · 8 min

Ask for a counterexample before trusting the conclusion

A strong prompt should actively search for evidence that could overturn its preferred answer.

Before finalising, give the strongest counterexample or conflicting evidence.
State whether it changes the recommendation and why.
TRY ITAdd a counterexample instruction to a recommendation prompt and predict what kind of overconfidence it should catch.
💡

Do not ask only for pros and cons; ask what evidence would actually change the decision.

🤖 PromptingStretch · 10 min

Separate answer generation from verification

A second explicit verification pass catches unsupported claims and missing constraints more reliably than one blended instruction.

PASS 1: Produce the best answer.
PASS 2: Audit every material claim against the supplied evidence.
Label unsupported claims and revise them before returning the final answer.
TRY ITTake one high-stakes prompt and split it into generation and verification passes with a clear audit criterion.
💡

Verification works best when the audit has a concrete failure condition, not just “double-check this”.

🧱 HTMLQuick win · 7 min

Use details/summary for native disclosure

Native disclosure gives keyboard and semantic behaviour without custom JavaScript.

<details>
  <summary>Shipping details</summary>
  <p>Ships in 2–3 days.</p>
</details>
TRY ITReplace one imaginary show/hide FAQ div with details and summary, then explain what behaviour the browser now owns.
💡

Reach for native interactive HTML before rebuilding the same behaviour with generic elements.

🧱 HTMLStretch · 9 min

Use data-* for small element metadata

Custom data attributes can attach simple application metadata to elements without inventing invalid attributes.

<button data-order-id="42">Open order</button>
<script>
  // button.dataset.orderId === "42"
</script>
TRY ITAdd a data attribute to a list item that JavaScript could read later, while keeping visible meaning in normal HTML.
💡

Use data-* for lightweight element metadata, not as a replacement for semantic content or a database.

🌿 Git & GitHubQuick win · 8 min

Stash only when interruption is real

git stash can park incomplete changes temporarily when you must switch context before the work deserves a commit.

git status
git stash push -m "wip: pricing experiment"
git stash list
TRY ITDescribe one case where a temporary stash is cleaner than a fake WIP commit, and one where a real commit is better.
💡

Do not let stashes become an invisible long-term backlog; name them and clear them deliberately.

🌿 Git & GitHubStretch · 12 min

Binary-search a regression with git bisect

When a bug appeared somewhere in a long commit range, bisect can systematically narrow the first bad commit.

git bisect start
git bisect bad
git bisect good <known-good-sha>
# test each midpoint, then mark good/bad
TRY ITExplain how many checks binary search roughly needs for 64 candidate commits and what makes the test reliable.
💡

Bisect is only as good as your repeatable good/bad test; automate that test when possible.

🔌 APIs & JSONQuick win · 9 min

Cancel stale requests with AbortController

Search and navigation can make an older request irrelevant; cancellation avoids wasted work and stale UI updates.

const controller = new AbortController();
fetch(url, { signal: controller.signal });
controller.abort();
TRY ITDescribe a typeahead-search scenario where request A should be cancelled after request B starts.
💡

Cancellation complements—not replaces—server timeouts and response ordering safeguards.

🔌 APIs & JSONStretch · 12 min

Protect retried writes with idempotency

Network uncertainty can cause clients to retry a write even when the first request actually succeeded.

POST /payments
Idempotency-Key: 7d9b...

{ "amount": 1499 }
TRY ITExplain why a payment create request is riskier to retry than a GET and what the server must remember for an idempotency key.
💡

Idempotency is a server contract: a client-supplied key helps only if the server stores and reuses the original outcome safely.