<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://shardalearningcenter.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://shardalearningcenter.github.io/" rel="alternate" type="text/html" /><updated>2026-07-10T15:50:54+00:00</updated><id>https://shardalearningcenter.github.io/feed.xml</id><title type="html">Sharda Learning Center</title><subtitle>Learn to code by building real projects. Fast. Practical. No fluff.</subtitle><entry><title type="html">Getting Started with Docker</title><link href="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-docker/" rel="alternate" type="text/html" title="Getting Started with Docker" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/getting-started-with-docker</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-docker/"><![CDATA[<p>Docker packages “it works on my machine” into something you can ship. For ML and APIs, that means: same Python, same system libraries, same entrypoint — on your laptop and in CI. This post takes you through the entire lifecycle once: build, run, verify, inspect, stop, and clean up — the part most tutorials skip.</p>

<h2 id="concepts">Concepts</h2>

<table>
  <thead>
    <tr>
      <th>Term</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Image</strong></td>
      <td>Immutable filesystem + metadata (the recipe result)</td>
    </tr>
    <tr>
      <td><strong>Container</strong></td>
      <td>A running (or stopped) instance of an image</td>
    </tr>
    <tr>
      <td><strong>Dockerfile</strong></td>
      <td>Instructions to build an image, executed top to bottom</td>
    </tr>
    <tr>
      <td><strong>Layer</strong></td>
      <td>Each Dockerfile instruction adds a cached layer; unchanged layers are reused on rebuild</td>
    </tr>
    <tr>
      <td><strong>Volume</strong></td>
      <td>Persistent data living outside the container’s writable layer</td>
    </tr>
  </tbody>
</table>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">--rm</span> <span class="nt">-it</span> python:3.12-slim bash
<span class="c"># inside the container: python --version; exit</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">--rm</code> deletes the container the moment it exits. <code class="language-plaintext highlighter-rouge">-it</code> gives you an interactive terminal attached to it.</p>

<h2 id="mini-project-build-run-verify-inspect-clean-up">Mini project: build, run, verify, inspect, clean up</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>docker-hello <span class="o">&amp;&amp;</span> <span class="nb">cd </span>docker-hello

<span class="nb">cat</span> <span class="o">&gt;</span> requirements.txt <span class="o">&lt;&lt;</span><span class="sh">'</span><span class="no">EOF</span><span class="sh">'
fastapi
uvicorn[standard]
</span><span class="no">EOF

</span><span class="nb">cat</span> <span class="o">&gt;</span> main.py <span class="o">&lt;&lt;</span><span class="sh">'</span><span class="no">EOF</span><span class="sh">'
from fastapi import FastAPI
app = FastAPI()

@app.get("/health")
def health():
    return {"ok": True}
</span><span class="no">EOF

</span><span class="nb">cat</span> <span class="o">&gt;</span> Dockerfile <span class="o">&lt;&lt;</span><span class="sh">'</span><span class="no">EOF</span><span class="sh">'
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
</span><span class="no">EOF

</span><span class="nb">cat</span> <span class="o">&gt;</span> .dockerignore <span class="o">&lt;&lt;</span><span class="sh">'</span><span class="no">EOF</span><span class="sh">'
.venv
__pycache__
*.pyc
.git
</span><span class="no">EOF
</span></code></pre></div></div>

<p>Dependencies are installed <strong>before</strong> <code class="language-plaintext highlighter-rouge">COPY . .</code> on purpose: editing <code class="language-plaintext highlighter-rouge">main.py</code> later won’t bust the pip-install cache layer, so rebuilds stay fast.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker build <span class="nt">-t</span> hello-api <span class="nb">.</span>
docker run <span class="nt">-d</span> <span class="nt">--name</span> hello <span class="nt">--rm</span> <span class="nt">-p</span> 8000:8000 hello-api

curl <span class="nt">-s</span> http://127.0.0.1:8000/health          <span class="c"># {"ok":true}</span>
docker logs hello                              <span class="c"># confirms uvicorn actually started</span>
docker <span class="nb">exec</span> <span class="nt">-it</span> hello bash <span class="nt">-c</span> <span class="s2">"python --version"</span>   <span class="c"># peek inside the running container</span>

docker stop hello                              <span class="c"># --rm means this also deletes the container</span>
docker ps <span class="nt">-a</span>                                   <span class="c"># confirm it's gone, not just stopped</span>
</code></pre></div></div>

<p>Cleanup — useful once containers and images pile up:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker images                 <span class="c"># see what's on disk</span>
docker system prune <span class="nt">-f</span>        <span class="c"># removes stopped containers, dangling images, unused networks</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">docker system prune</code> is destructive for anything not currently running or tagged as in-use — read what it says it will remove before you run it on a machine with work you care about.</p>

<h2 id="habits-that-save-pain">Habits that save pain</h2>

<ul>
  <li>Pin base images (<code class="language-plaintext highlighter-rouge">python:3.12.8-slim</code>) when reproducibility matters; <code class="language-plaintext highlighter-rouge">latest</code> silently changes under you.</li>
  <li>Put dependency installs before <code class="language-plaintext highlighter-rouge">COPY . .</code> (done above) so code edits don’t bust the pip cache every time.</li>
  <li>Don’t bake secrets into images — pass environment variables or mount secrets at runtime, since anything in a layer is recoverable from the image history.</li>
  <li>Prefer one process per container; if you need two processes, you probably need two containers plus <code class="language-plaintext highlighter-rouge">docker compose</code>, not a supervisor script.</li>
  <li>Always ship a <code class="language-plaintext highlighter-rouge">.dockerignore</code> — without one, <code class="language-plaintext highlighter-rouge">COPY . .</code> pulls in <code class="language-plaintext highlighter-rouge">.git</code>, <code class="language-plaintext highlighter-rouge">.venv</code>, and <code class="language-plaintext highlighter-rouge">node_modules</code>, bloating the build context and sometimes leaking history into the image.</li>
</ul>

<h2 id="common-footguns">Common footguns</h2>

<ul>
  <li><strong>“permission denied” on <code class="language-plaintext highlighter-rouge">/var/run/docker.sock</code></strong> — on Linux, add your user to the <code class="language-plaintext highlighter-rouge">docker</code> group (<code class="language-plaintext highlighter-rouge">sudo usermod -aG docker $USER</code>, then log out/in) instead of prefixing every command with <code class="language-plaintext highlighter-rouge">sudo</code>.</li>
  <li><strong>“port is already allocated”</strong> — something else is bound to <code class="language-plaintext highlighter-rouge">8000</code>; either stop it (<code class="language-plaintext highlighter-rouge">docker ps</code> to find the culprit) or map a different host port (<code class="language-plaintext highlighter-rouge">-p 8001:8000</code>).</li>
  <li><strong>Forgetting <code class="language-plaintext highlighter-rouge">--rm</code></strong> — stopped containers accumulate silently and eat disk; <code class="language-plaintext highlighter-rouge">docker ps -a</code> reveals them, <code class="language-plaintext highlighter-rouge">docker container prune</code> clears them.</li>
  <li><strong>Root by default</strong> — containers run as root unless you add a <code class="language-plaintext highlighter-rouge">USER</code> instruction; fine for local learning, a real problem for anything internet-facing.</li>
  <li><strong>Rebuilding without noticing cache reuse</strong> — if you change <code class="language-plaintext highlighter-rouge">requirements.txt</code>, Docker reruns <code class="language-plaintext highlighter-rouge">pip install</code>; if you only change <code class="language-plaintext highlighter-rouge">main.py</code>, it reuses the cached install layer. Confusing these two is a common “why is this taking so long” moment.</li>
</ul>

<h2 id="you-know-youre-done-when">You know you’re done when…</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">docker build</code> finishes with no errors and <code class="language-plaintext highlighter-rouge">docker images</code> lists <code class="language-plaintext highlighter-rouge">hello-api</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">curl http://127.0.0.1:8000/health</code> returns <code class="language-plaintext highlighter-rouge">{"ok":true}</code> while the container runs detached (<code class="language-plaintext highlighter-rouge">-d</code>)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">docker logs hello</code> shows the uvicorn startup line, proving the process actually started (not just that the container exists)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />After <code class="language-plaintext highlighter-rouge">docker stop hello</code>, <code class="language-plaintext highlighter-rouge">docker ps -a</code> shows it gone (because of <code class="language-plaintext highlighter-rouge">--rm</code>), not lingering as “Exited”</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You have a <code class="language-plaintext highlighter-rouge">.dockerignore</code> so <code class="language-plaintext highlighter-rouge">.git</code> and <code class="language-plaintext highlighter-rouge">.venv</code> never enter the build context</li>
</ul>

<h2 id="next">Next</h2>

<p><a href="/2026/07/10/getting-started-with-fastapi/">Getting Started with FastAPI</a> — the API you just containerized, explained properly · wrap models behind HTTP after <a href="/courses/llm-mastery/">LLM Mastery</a>.</p>]]></content><author><name></name></author><category term="docker" /><category term="getting-started" /><summary type="html"><![CDATA[Images vs containers, a minimal Dockerfile, and a full build-run-inspect-cleanup cycle for a tiny Python API — the same one every time.]]></summary></entry><entry><title type="html">Getting Started with FastAPI</title><link href="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-fastapi/" rel="alternate" type="text/html" title="Getting Started with FastAPI" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/getting-started-with-fastapi</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-fastapi/"><![CDATA[<p>FastAPI is a pleasant way to wrap models and scripts behind HTTP. Auto docs, type hints, and Pydantic validation make it a strong default for LLM demos and internal tools. This post builds a small but real API with four routes and deliberately exercises its error paths — a 404 and a 422 you trigger yourself, not just the happy path.</p>

<h2 id="install">Install</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pip <span class="nb">install </span>fastapi uvicorn
</code></pre></div></div>

<h2 id="mini-project-a-notes-api-with-real-validation-and-error-handling">Mini project: a notes API with real validation and error handling</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># main.py
</span><span class="kn">from</span> <span class="nn">fastapi</span> <span class="kn">import</span> <span class="n">FastAPI</span><span class="p">,</span> <span class="n">HTTPException</span>
<span class="kn">from</span> <span class="nn">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">Field</span>

<span class="n">app</span> <span class="o">=</span> <span class="n">FastAPI</span><span class="p">(</span><span class="n">title</span><span class="o">=</span><span class="s">"Notes API"</span><span class="p">)</span>


<span class="k">class</span> <span class="nc">Note</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
    <span class="nb">id</span><span class="p">:</span> <span class="nb">int</span>
    <span class="n">text</span><span class="p">:</span> <span class="nb">str</span>


<span class="k">class</span> <span class="nc">NoteIn</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
    <span class="n">text</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Field</span><span class="p">(</span><span class="n">min_length</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">max_length</span><span class="o">=</span><span class="mi">500</span><span class="p">)</span>


<span class="n">notes</span><span class="p">:</span> <span class="nb">dict</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="n">Note</span><span class="p">]</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">next_id</span> <span class="o">=</span> <span class="mi">1</span>


<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"/health"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">health</span><span class="p">():</span>
    <span class="k">return</span> <span class="p">{</span><span class="s">"ok"</span><span class="p">:</span> <span class="bp">True</span><span class="p">}</span>


<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"/notes"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">list_notes</span><span class="p">():</span>
    <span class="k">return</span> <span class="nb">list</span><span class="p">(</span><span class="n">notes</span><span class="p">.</span><span class="n">values</span><span class="p">())</span>


<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">post</span><span class="p">(</span><span class="s">"/notes"</span><span class="p">,</span> <span class="n">status_code</span><span class="o">=</span><span class="mi">201</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">create_note</span><span class="p">(</span><span class="n">body</span><span class="p">:</span> <span class="n">NoteIn</span><span class="p">):</span>
    <span class="k">global</span> <span class="n">next_id</span>
    <span class="n">note</span> <span class="o">=</span> <span class="n">Note</span><span class="p">(</span><span class="nb">id</span><span class="o">=</span><span class="n">next_id</span><span class="p">,</span> <span class="n">text</span><span class="o">=</span><span class="n">body</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
    <span class="n">notes</span><span class="p">[</span><span class="n">next_id</span><span class="p">]</span> <span class="o">=</span> <span class="n">note</span>
    <span class="n">next_id</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="n">note</span>


<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"/notes/{note_id}"</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_note</span><span class="p">(</span><span class="n">note_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">note_id</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">notes</span><span class="p">:</span>
        <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s">"note not found"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">notes</span><span class="p">[</span><span class="n">note_id</span><span class="p">]</span>


<span class="o">@</span><span class="n">app</span><span class="p">.</span><span class="n">delete</span><span class="p">(</span><span class="s">"/notes/{note_id}"</span><span class="p">,</span> <span class="n">status_code</span><span class="o">=</span><span class="mi">204</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">delete_note</span><span class="p">(</span><span class="n">note_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">note_id</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">notes</span><span class="p">:</span>
        <span class="k">raise</span> <span class="n">HTTPException</span><span class="p">(</span><span class="n">status_code</span><span class="o">=</span><span class="mi">404</span><span class="p">,</span> <span class="n">detail</span><span class="o">=</span><span class="s">"note not found"</span><span class="p">)</span>
    <span class="k">del</span> <span class="n">notes</span><span class="p">[</span><span class="n">note_id</span><span class="p">]</span>
</code></pre></div></div>

<p>Run it and open the auto-generated docs:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>uvicorn main:app <span class="nt">--reload</span> <span class="nt">--port</span> 8000
<span class="c"># open http://127.0.0.1:8000/docs</span>
</code></pre></div></div>

<p>Now exercise every path with <code class="language-plaintext highlighter-rouge">curl</code>, including the two that should fail on purpose:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> http://127.0.0.1:8000/health
<span class="c"># {"ok":true}</span>

curl <span class="nt">-s</span> <span class="nt">-X</span> POST http://127.0.0.1:8000/notes <span class="se">\</span>
  <span class="nt">-H</span> <span class="s1">'content-type: application/json'</span> <span class="nt">-d</span> <span class="s1">'{"text":"buy milk"}'</span>
<span class="c"># {"id":1,"text":"buy milk"}  — check the HTTP status too:</span>

curl <span class="nt">-s</span> <span class="nt">-o</span> /dev/null <span class="nt">-w</span> <span class="s2">"%{http_code}</span><span class="se">\n</span><span class="s2">"</span> <span class="nt">-X</span> POST http://127.0.0.1:8000/notes <span class="se">\</span>
  <span class="nt">-H</span> <span class="s1">'content-type: application/json'</span> <span class="nt">-d</span> <span class="s1">'{"text":"buy eggs"}'</span>
<span class="c"># 201, not the default 200 — because of status_code=201 on the route</span>

curl <span class="nt">-s</span> http://127.0.0.1:8000/notes
<span class="c"># [{"id":1,...},{"id":2,...}]</span>

curl <span class="nt">-s</span> <span class="nt">-o</span> /dev/null <span class="nt">-w</span> <span class="s2">"%{http_code}</span><span class="se">\n</span><span class="s2">"</span> http://127.0.0.1:8000/notes/999
<span class="c"># 404 — an id that was never created</span>

curl <span class="nt">-s</span> <span class="nt">-X</span> POST http://127.0.0.1:8000/notes <span class="se">\</span>
  <span class="nt">-H</span> <span class="s1">'content-type: application/json'</span> <span class="nt">-d</span> <span class="s1">'{"text":""}'</span> <span class="se">\</span>
  <span class="nt">-o</span> /dev/null <span class="nt">-w</span> <span class="s2">"%{http_code}</span><span class="se">\n</span><span class="s2">"</span>
<span class="c"># 422 — Pydantic's min_length=1 rejected it before your code ever ran</span>

curl <span class="nt">-s</span> <span class="nt">-X</span> DELETE http://127.0.0.1:8000/notes/1 <span class="nt">-o</span> /dev/null <span class="nt">-w</span> <span class="s2">"%{http_code}</span><span class="se">\n</span><span class="s2">"</span>
<span class="c"># 204</span>
</code></pre></div></div>

<p>If any of those status codes don’t match, that’s the bug to chase — the point of writing them down is having something concrete to check against, not “it looked like it worked in the docs UI.”</p>

<h2 id="why-it-fits-ai-work">Why it fits AI work</h2>

<table>
  <thead>
    <tr>
      <th>Need</th>
      <th>FastAPI habit</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Validate inputs before they hit your model</td>
      <td>Pydantic models (the <code class="language-plaintext highlighter-rouge">NoteIn</code> pattern above)</td>
    </tr>
    <tr>
      <td>Long model calls</td>
      <td>background tasks or a job queue, not a blocking route</td>
    </tr>
    <tr>
      <td>Ship consistently</td>
      <td><a href="/2026/07/10/getting-started-with-docker/">Docker</a> + uvicorn</td>
    </tr>
    <tr>
      <td>Explore the API while building</td>
      <td><code class="language-plaintext highlighter-rouge">/docs</code> (Swagger UI) — try requests directly from the browser</td>
    </tr>
  </tbody>
</table>

<p>Keep inference or business logic in a plain Python module; the route itself should stay thin: validate → call → return.</p>

<h2 id="common-footguns">Common footguns</h2>

<ul>
  <li><strong>In-memory state isn’t persistence</strong> — the <code class="language-plaintext highlighter-rouge">notes</code> dict resets to empty every time <code class="language-plaintext highlighter-rouge">uvicorn --reload</code> restarts (which it does automatically on file changes). This is fine for learning; for anything real, back it with the SQLite schema from <a href="/2026/07/10/getting-started-with-sql/">Getting Started with SQL</a>.</li>
  <li><strong>Blocking code inside <code class="language-plaintext highlighter-rouge">async def</code></strong> — a synchronous call like <code class="language-plaintext highlighter-rouge">time.sleep()</code> or heavy file I/O inside an <code class="language-plaintext highlighter-rouge">async def</code> route blocks the <em>entire</em> event loop, stalling every other request. Use a plain <code class="language-plaintext highlighter-rouge">def</code> route for sync work (FastAPI runs it in a thread pool automatically) or <code class="language-plaintext highlighter-rouge">await</code> a genuinely async call.</li>
  <li><strong>Forgetting the default status code</strong> — routes return <code class="language-plaintext highlighter-rouge">200</code> unless you set <code class="language-plaintext highlighter-rouge">status_code=</code> explicitly; a <code class="language-plaintext highlighter-rouge">POST</code> that creates a resource should return <code class="language-plaintext highlighter-rouge">201</code>, a <code class="language-plaintext highlighter-rouge">DELETE</code> typically <code class="language-plaintext highlighter-rouge">204</code>.</li>
  <li><strong>Trusting raw <code class="language-plaintext highlighter-rouge">dict</code>/<code class="language-plaintext highlighter-rouge">request.json()</code> instead of Pydantic</strong> — skips validation entirely, so malformed input reaches your business logic instead of failing fast with a clear <code class="language-plaintext highlighter-rouge">422</code>.</li>
  <li><strong>CORS errors when calling from a browser on a different origin</strong> — add <code class="language-plaintext highlighter-rouge">CORSMiddleware</code> explicitly; FastAPI doesn’t allow cross-origin requests by default, on purpose.</li>
  <li><strong>Leaving <code class="language-plaintext highlighter-rouge">--reload</code> on outside development</strong> — it watches the filesystem and restarts the process, which is convenient locally and inappropriate (and slower) in production.</li>
</ul>

<h2 id="you-know-youre-done-when">You know you’re done when…</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">GET /health</code> returns <code class="language-plaintext highlighter-rouge">{"ok": true}</code> with status <code class="language-plaintext highlighter-rouge">200</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">POST /notes</code> with a valid body returns status <code class="language-plaintext highlighter-rouge">201</code> and an incrementing <code class="language-plaintext highlighter-rouge">id</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">GET /notes/999</code> (an id that doesn’t exist) returns <code class="language-plaintext highlighter-rouge">404</code>, not a <code class="language-plaintext highlighter-rouge">500</code> or an empty <code class="language-plaintext highlighter-rouge">200</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">POST /notes</code> with <code class="language-plaintext highlighter-rouge">{"text": ""}</code> returns <code class="language-plaintext highlighter-rouge">422</code> because of Pydantic’s <code class="language-plaintext highlighter-rouge">min_length</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can explain why restarting <code class="language-plaintext highlighter-rouge">uvicorn --reload</code> wipes your notes, and what you’d change to make them survive a restart</li>
</ul>

<h2 id="next">Next</h2>

<p>Serve a tiny model after <a href="/courses/llm-mastery/">LLM Mastery</a> (quantization &amp; serving in article 44). For product UIs, pair with <a href="/2026/07/10/getting-started-with-javascript/">Getting Started with JavaScript</a>.</p>]]></content><author><name></name></author><category term="fastapi" /><category term="python" /><category term="getting-started" /><summary type="html"><![CDATA[Build a real in-memory notes API — GET, POST, DELETE, validation errors, 404s — and check every response code with curl, not vibes.]]></summary></entry><entry><title type="html">Getting Started with Git (without the fear)</title><link href="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-git/" rel="alternate" type="text/html" title="Getting Started with Git (without the fear)" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/getting-started-with-git</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-git/"><![CDATA[<p>Git is a time machine for your files. Most “Git anxiety” comes from never having deliberately broken something and fixed it. This post has you create a real merge conflict on purpose, resolve it, and confirm the history is sane — because that’s the moment Git stops feeling like magic.</p>

<h2 id="install-and-one-time-setup">Install and one-time setup</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git <span class="nt">--version</span>
git config <span class="nt">--global</span> user.name <span class="s2">"Your Name"</span>
git config <span class="nt">--global</span> user.email <span class="s2">"you@example.com"</span>
git config <span class="nt">--global</span> init.defaultBranch main
</code></pre></div></div>

<p>Skip the global config and your commits show up anonymously as whatever your OS username happens to be — annoying to fix retroactively across dozens of commits.</p>

<h2 id="daily-driver">Daily driver</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git status                 <span class="c"># where am I?</span>
git add <span class="nt">-p</span>                 <span class="c"># stage hunks intentionally, not blindly</span>
git commit <span class="nt">-m</span> <span class="s2">"message"</span>    <span class="c"># snapshot with a why</span>
git log <span class="nt">--oneline</span> <span class="nt">-10</span>      <span class="c"># recent history</span>
git diff                   <span class="c"># unstaged changes</span>
git diff <span class="nt">--staged</span>          <span class="c"># what will actually be committed</span>
</code></pre></div></div>

<p>Commit messages: say <strong>why</strong>, not “update files.” Future-you (and reviewers) need the reason, not a restatement of the diff.</p>

<h2 id="mental-model">Mental model</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>working tree  →  staging area  →  commit (immutable snapshot)
     edit            git add           git commit
</code></pre></div></div>

<ul>
  <li>A <strong>branch</strong> is a movable label pointing at a commit — nothing more exotic than that.</li>
  <li><code class="language-plaintext highlighter-rouge">main</code> is a convention, not a special object; Git doesn’t know it’s “the important one.”</li>
  <li>Remotes (<code class="language-plaintext highlighter-rouge">origin</code>) are other copies of the same commit graph, not a separate system.</li>
</ul>

<h2 id="mini-project-create-a-merge-conflict-then-resolve-it">Mini project: create a merge conflict, then resolve it</h2>

<p>Tutorials that avoid conflicts teach you to fear them. Do this instead — it’s the single most useful 5 minutes you can spend learning Git.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>git-practice <span class="o">&amp;&amp;</span> <span class="nb">cd </span>git-practice
git init <span class="nt">-b</span> main
<span class="nb">echo</span> <span class="s2">"Headline: draft"</span> <span class="o">&gt;</span> readme.txt
git add readme.txt
git commit <span class="nt">-m</span> <span class="s2">"add readme with draft headline"</span>

git switch <span class="nt">-c</span> feature-headline
<span class="nb">echo</span> <span class="s2">"Headline: Hello World"</span> <span class="o">&gt;</span> readme.txt
git commit <span class="nt">-am</span> <span class="s2">"feature: rewrite headline for launch"</span>

git switch main
<span class="nb">echo</span> <span class="s2">"Headline: Welcome Friends"</span> <span class="o">&gt;</span> readme.txt
git commit <span class="nt">-am</span> <span class="s2">"main: rewrite headline for homepage"</span>

git merge feature-headline
</code></pre></div></div>

<p>Git stops and reports <code class="language-plaintext highlighter-rouge">CONFLICT (content): Merge conflict in readme.txt</code>. Open the file — it now looks like this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD
Headline: Welcome Friends
=======
Headline: Hello World
&gt;&gt;&gt;&gt;&gt;&gt;&gt; feature-headline
</code></pre></div></div>

<p>Everything between <code class="language-plaintext highlighter-rouge">&lt;&lt;&lt;&lt;&lt;&lt;&lt;</code> and <code class="language-plaintext highlighter-rouge">=======</code> is your current branch (<code class="language-plaintext highlighter-rouge">HEAD</code>); everything between <code class="language-plaintext highlighter-rouge">=======</code> and <code class="language-plaintext highlighter-rouge">&gt;&gt;&gt;&gt;&gt;&gt;&gt;</code> is the branch you’re merging in. Resolve it by editing the file down to what you actually want — say, <code class="language-plaintext highlighter-rouge">Headline: Welcome Friends (Hello World!)</code> — deleting the marker lines, then:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git add readme.txt
git commit <span class="nt">--no-edit</span>          <span class="c"># finishes the merge commit</span>
git log <span class="nt">--oneline</span> <span class="nt">--graph</span> <span class="nt">--all</span>
</code></pre></div></div>

<p>The graph should show two branches converging into one merge commit. You created the exact situation every team hits weekly, and fixed it without panic.</p>

<h2 id="undo-without-panic">Undo without panic</h2>

<table>
  <thead>
    <tr>
      <th>Situation</th>
      <th>Safe move</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Unstaged edits you hate</td>
      <td><code class="language-plaintext highlighter-rouge">git restore file</code></td>
    </tr>
    <tr>
      <td>Staged too much</td>
      <td><code class="language-plaintext highlighter-rouge">git restore --staged file</code></td>
    </tr>
    <tr>
      <td>Last commit message wrong</td>
      <td><code class="language-plaintext highlighter-rouge">git commit --amend</code> (only if not pushed)</td>
    </tr>
    <tr>
      <td>Need a throwaway experiment</td>
      <td>new branch; delete it later</td>
    </tr>
    <tr>
      <td>“I think I deleted a commit”</td>
      <td><code class="language-plaintext highlighter-rouge">git reflog</code> — find the old <code class="language-plaintext highlighter-rouge">HEAD</code> position, then <code class="language-plaintext highlighter-rouge">git switch -c rescue-branch &lt;sha&gt;</code></td>
    </tr>
  </tbody>
</table>

<p><code class="language-plaintext highlighter-rouge">git reflog</code> is your safety net: Git keeps a log of where <code class="language-plaintext highlighter-rouge">HEAD</code> has pointed, even across resets and rebases, for about 90 days by default. Almost nothing is truly lost immediately.</p>

<h2 id="common-footguns">Common footguns</h2>

<ul>
  <li><strong>Force-pushing shared branches</strong> — <code class="language-plaintext highlighter-rouge">git push --force</code> overwrites history your teammates already pulled. Use <code class="language-plaintext highlighter-rouge">--force-with-lease</code> if you must, and only on branches you own.</li>
  <li><strong>Detached HEAD confusion</strong> — <code class="language-plaintext highlighter-rouge">git checkout &lt;sha&gt;</code> (not a branch) puts you in detached HEAD. Commits there are orphaned unless you <code class="language-plaintext highlighter-rouge">git switch -c &lt;name&gt;</code> before leaving.</li>
  <li><strong>Committing secrets or large binaries</strong> — they live in history forever unless you rewrite it (<code class="language-plaintext highlighter-rouge">git filter-repo</code>), which is painful. <code class="language-plaintext highlighter-rouge">.gitignore</code> them before the first commit, not after.</li>
  <li><strong>Rebasing after pushing</strong> — rewrites commit SHAs your collaborators already have, forcing them into confusing conflicts. Rebase local, unpublished work only.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">.gitignore</code> added too late</strong> — adding a pattern doesn’t untrack files already committed; you also need <code class="language-plaintext highlighter-rouge">git rm --cached &lt;file&gt;</code>.</li>
</ul>

<h2 id="you-know-youre-done-when">You know you’re done when…</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">git log --oneline --graph --all</code> shows both branches merging into a single history</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You deliberately created and resolved a real merge conflict, not just avoided one</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">git status</code> is clean before you switch branches (no surprise stashing later)</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can find a “lost” commit with <code class="language-plaintext highlighter-rouge">git reflog</code> without googling the command</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">user.name</code> / <code class="language-plaintext highlighter-rouge">user.email</code> are configured, so your commits aren’t anonymous</li>
</ul>

<h2 id="next">Next</h2>

<p>Use Git on every course project. Pair with <a href="/2026/07/10/getting-started-with-linux-shell/">Getting Started with the Linux Shell</a> so <code class="language-plaintext highlighter-rouge">git</code> and the terminal feel like one tool.</p>]]></content><author><name></name></author><category term="git" /><category term="getting-started" /><summary type="html"><![CDATA[Five daily commands, a clear mental model, and a real merge conflict you create and resolve yourself — enough Git to stop being afraid of it.]]></summary></entry><entry><title type="html">Getting Started with JavaScript</title><link href="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-javascript/" rel="alternate" type="text/html" title="Getting Started with JavaScript" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/getting-started-with-javascript</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-javascript/"><![CDATA[<p>JavaScript runs in the browser and on the server (Node). Start in the browser console (<code class="language-plaintext highlighter-rouge">F12</code> → Console), then move to a file once a snippet outgrows one line. This post ends with a small app that actually persists data across page reloads — the clearest way to prove your code did what you think it did.</p>

<h2 id="essentials">Essentials</h2>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">name</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Sharda</span><span class="dl">"</span><span class="p">;</span>     <span class="c1">// prefer const</span>
<span class="kd">let</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>             <span class="c1">// when a value must change</span>

<span class="kd">const</span> <span class="nx">add</span> <span class="o">=</span> <span class="p">(</span><span class="nx">a</span><span class="p">,</span> <span class="nx">b</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">a</span> <span class="o">+</span> <span class="nx">b</span><span class="p">;</span>

<span class="kd">const</span> <span class="nx">nums</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">];</span>
<span class="kd">const</span> <span class="nx">evens</span> <span class="o">=</span> <span class="nx">nums</span><span class="p">.</span><span class="nx">filter</span><span class="p">((</span><span class="nx">n</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">===</span> <span class="mi">0</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">doubled</span> <span class="o">=</span> <span class="nx">nums</span><span class="p">.</span><span class="nx">map</span><span class="p">((</span><span class="nx">n</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">n</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span>

<span class="kd">const</span> <span class="p">{</span> <span class="nx">id</span><span class="p">,</span> <span class="nx">role</span> <span class="p">}</span> <span class="o">=</span> <span class="p">{</span> <span class="na">id</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="na">role</span><span class="p">:</span> <span class="dl">"</span><span class="s2">student</span><span class="dl">"</span> <span class="p">};</span>   <span class="c1">// destructuring</span>
<span class="kd">const</span> <span class="nx">label</span> <span class="o">=</span> <span class="s2">`user #</span><span class="p">${</span><span class="nx">id</span><span class="p">}</span><span class="s2"> (</span><span class="p">${</span><span class="nx">role</span><span class="p">}</span><span class="s2">)`</span><span class="p">;</span>              <span class="c1">// template literal</span>
</code></pre></div></div>

<p>Always use <code class="language-plaintext highlighter-rouge">===</code>/<code class="language-plaintext highlighter-rouge">!==</code>, never <code class="language-plaintext highlighter-rouge">==</code>/<code class="language-plaintext highlighter-rouge">!=</code>. Loose equality coerces types in ways that surprise you (<code class="language-plaintext highlighter-rouge">"0" == false</code> is <code class="language-plaintext highlighter-rouge">true</code>).</p>

<h2 id="async-without-tears">Async without tears</h2>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="kd">function</span> <span class="nx">load</span><span class="p">()</span> <span class="p">{</span>
  <span class="kd">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="dl">"</span><span class="s2">https://httpbin.org/get</span><span class="dl">"</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">res</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">`HTTP </span><span class="p">${</span><span class="nx">res</span><span class="p">.</span><span class="nx">status</span><span class="p">}</span><span class="s2">`</span><span class="p">);</span>
  <span class="k">return</span> <span class="k">await</span> <span class="nx">res</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
<span class="p">}</span>

<span class="nx">load</span><span class="p">().</span><span class="nx">then</span><span class="p">(</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">).</span><span class="k">catch</span><span class="p">(</span><span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">);</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">async</code>/<code class="language-plaintext highlighter-rouge">await</code> and Promises are the same idea: “do this when the network returns,” written to read top-to-bottom instead of nested in callbacks. The one rule that prevents most bugs: every Promise needs either an <code class="language-plaintext highlighter-rouge">await</code>, a <code class="language-plaintext highlighter-rouge">.then()</code>, or an explicit <code class="language-plaintext highlighter-rouge">.catch()</code> — an unhandled one (“floating promise”) fails silently and you find out in production.</p>

<h2 id="mini-project-a-todo-app-that-survives-a-page-reload">Mini project: a todo app that survives a page reload</h2>

<p>DOM manipulation and <code class="language-plaintext highlighter-rouge">localStorage</code> together, with zero dependencies and zero build step. Save this as <code class="language-plaintext highlighter-rouge">todo.html</code> and open it directly in a browser.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="nt">&lt;html&gt;</span>
<span class="nt">&lt;head&gt;&lt;title&gt;</span>Todos<span class="nt">&lt;/title&gt;&lt;/head&gt;</span>
<span class="nt">&lt;body&gt;</span>
  <span class="nt">&lt;input</span> <span class="na">id=</span><span class="s">"task"</span> <span class="na">placeholder=</span><span class="s">"New task"</span> <span class="nt">/&gt;</span>
  <span class="nt">&lt;button</span> <span class="na">id=</span><span class="s">"add"</span><span class="nt">&gt;</span>Add<span class="nt">&lt;/button&gt;</span>
  <span class="nt">&lt;ul</span> <span class="na">id=</span><span class="s">"list"</span><span class="nt">&gt;&lt;/ul&gt;</span>

  <span class="nt">&lt;script&gt;</span>
    <span class="kd">const</span> <span class="nx">KEY</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">todos-v1</span><span class="dl">"</span><span class="p">;</span>
    <span class="kd">const</span> <span class="nx">load</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">localStorage</span><span class="p">.</span><span class="nx">getItem</span><span class="p">(</span><span class="nx">KEY</span><span class="p">)</span> <span class="o">||</span> <span class="dl">"</span><span class="s2">[]</span><span class="dl">"</span><span class="p">);</span>
    <span class="kd">const</span> <span class="nx">save</span> <span class="o">=</span> <span class="p">(</span><span class="nx">todos</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nx">setItem</span><span class="p">(</span><span class="nx">KEY</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">todos</span><span class="p">));</span>

    <span class="kd">function</span> <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">list</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">list</span><span class="dl">"</span><span class="p">);</span>
      <span class="nx">list</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
      <span class="nx">load</span><span class="p">().</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">todo</span><span class="p">,</span> <span class="nx">i</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="kd">const</span> <span class="nx">li</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">li</span><span class="dl">"</span><span class="p">);</span>
        <span class="nx">li</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="nx">todo</span> <span class="o">+</span> <span class="dl">"</span><span class="s2"> </span><span class="dl">"</span><span class="p">;</span>

        <span class="kd">const</span> <span class="nx">del</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">"</span><span class="s2">button</span><span class="dl">"</span><span class="p">);</span>
        <span class="nx">del</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">x</span><span class="dl">"</span><span class="p">;</span>
        <span class="nx">del</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
          <span class="kd">const</span> <span class="nx">todos</span> <span class="o">=</span> <span class="nx">load</span><span class="p">();</span>
          <span class="nx">todos</span><span class="p">.</span><span class="nx">splice</span><span class="p">(</span><span class="nx">i</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
          <span class="nx">save</span><span class="p">(</span><span class="nx">todos</span><span class="p">);</span>
          <span class="nx">render</span><span class="p">();</span>
        <span class="p">};</span>

        <span class="nx">li</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">del</span><span class="p">);</span>
        <span class="nx">list</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">li</span><span class="p">);</span>
      <span class="p">});</span>
    <span class="p">}</span>

    <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">add</span><span class="dl">"</span><span class="p">).</span><span class="nx">onclick</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
      <span class="kd">const</span> <span class="nx">input</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">task</span><span class="dl">"</span><span class="p">);</span>
      <span class="kd">const</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">input</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">trim</span><span class="p">();</span>
      <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">value</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
      <span class="kd">const</span> <span class="nx">todos</span> <span class="o">=</span> <span class="nx">load</span><span class="p">();</span>
      <span class="nx">todos</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
      <span class="nx">save</span><span class="p">(</span><span class="nx">todos</span><span class="p">);</span>
      <span class="nx">input</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="dl">""</span><span class="p">;</span>
      <span class="nx">render</span><span class="p">();</span>
    <span class="p">};</span>

    <span class="nx">render</span><span class="p">();</span>
  <span class="nt">&lt;/script&gt;</span>
<span class="nt">&lt;/body&gt;</span>
<span class="nt">&lt;/html&gt;</span>
</code></pre></div></div>

<p>Test it end to end, in this order:</p>

<ol>
  <li>Open <code class="language-plaintext highlighter-rouge">todo.html</code>, add three tasks — they appear immediately.</li>
  <li><strong>Refresh the page.</strong> All three should still be there. This is the real test: it proves data persisted in <code class="language-plaintext highlighter-rouge">localStorage</code>, not just in a JavaScript variable that resets on reload.</li>
  <li>Delete the middle task. Confirm it removed <em>that one</em>, not its neighbor — an easy off-by-one bug in <code class="language-plaintext highlighter-rouge">splice(i, 1)</code> if <code class="language-plaintext highlighter-rouge">i</code> were computed wrong.</li>
  <li>Open DevTools → Application → Local Storage and inspect the raw JSON string yourself.</li>
</ol>

<p>Extend it: add an “edit” button, or a “clear completed” button using <code class="language-plaintext highlighter-rouge">.filter()</code> instead of <code class="language-plaintext highlighter-rouge">.splice()</code>.</p>

<h2 id="common-footguns">Common footguns</h2>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">==</code> vs <code class="language-plaintext highlighter-rouge">===</code></strong> — covered above, but it’s the single most common source of “why is this true when it shouldn’t be” bugs.</li>
  <li><strong>Losing <code class="language-plaintext highlighter-rouge">this</code> in callbacks</strong> — a plain <code class="language-plaintext highlighter-rouge">function</code> used as an event handler gets its own <code class="language-plaintext highlighter-rouge">this</code>; an arrow function captures the surrounding scope’s <code class="language-plaintext highlighter-rouge">this</code>. The todo app above uses arrow functions for exactly this reason.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">var</code> vs <code class="language-plaintext highlighter-rouge">let</code>/<code class="language-plaintext highlighter-rouge">const</code></strong> — <code class="language-plaintext highlighter-rouge">var</code> is function-scoped and hoists, causing loop-variable bugs (<code class="language-plaintext highlighter-rouge">for (var i ...)</code> inside async callbacks all seeing the final <code class="language-plaintext highlighter-rouge">i</code>). Default to <code class="language-plaintext highlighter-rouge">const</code>, use <code class="language-plaintext highlighter-rouge">let</code> only when reassigning, and avoid <code class="language-plaintext highlighter-rouge">var</code> entirely.</li>
  <li><strong>Floating promises</strong> — calling an <code class="language-plaintext highlighter-rouge">async</code> function without <code class="language-plaintext highlighter-rouge">await</code> or <code class="language-plaintext highlighter-rouge">.catch()</code> swallows errors silently. Lint rules like <code class="language-plaintext highlighter-rouge">no-floating-promises</code> exist specifically for this.</li>
  <li><strong>Mutating arrays you’re about to render from</strong> — <code class="language-plaintext highlighter-rouge">todos.push(...)</code> then forgetting to <code class="language-plaintext highlighter-rouge">save()</code> before <code class="language-plaintext highlighter-rouge">render()</code> leaves the UI and storage out of sync; the app above always saves, then renders.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">NaN !== NaN</code></strong> — the one value that isn’t equal to itself; use <code class="language-plaintext highlighter-rouge">Number.isNaN(x)</code> to check, never <code class="language-plaintext highlighter-rouge">x === NaN</code>.</li>
</ul>

<h2 id="you-know-youre-done-when">You know you’re done when…</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Adding a task updates the list immediately with no page reload</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Reloading the browser keeps your todos — proof <code class="language-plaintext highlighter-rouge">localStorage</code> persisted, not just in-memory state</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Deleting one todo removes exactly that item, not a neighbor</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />DevTools → Console shows zero errors during the whole flow</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can explain why the delete handler uses an arrow function rather than a plain <code class="language-plaintext highlighter-rouge">function</code></li>
</ul>

<h2 id="next">Next</h2>

<p>Frontends often call Python backends — see <a href="/2026/07/10/getting-started-with-fastapi/">Getting Started with FastAPI</a>. For AI product UIs, keep JS thin and put model logic on the server.</p>]]></content><author><name></name></author><category term="javascript" /><category term="getting-started" /><summary type="html"><![CDATA[Variables, async/await, and a persistent todo app built with vanilla JS and localStorage — no framework, no build step, fully checkable.]]></summary></entry><entry><title type="html">Getting Started with the Linux Shell</title><link href="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-linux-shell/" rel="alternate" type="text/html" title="Getting Started with the Linux Shell" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/getting-started-with-linux-shell</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-linux-shell/"><![CDATA[<p>The shell is how you talk to the machine without a mouse. Every serious ML and backend workflow eventually lands here: datasets, logs, Docker, SSH, CI. This post ends with you generating fake log data and writing pipelines that answer real questions about it — the same shape of work you’ll do against production logs.</p>

<h2 id="survival-kit">Survival kit</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">pwd</span>                 <span class="c"># where am I?</span>
<span class="nb">ls</span> <span class="nt">-la</span>               <span class="c"># what is here, including dotfiles?</span>
<span class="nb">cd </span>path              <span class="c"># move</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> a/b         <span class="c"># create nested dirs in one shot</span>
<span class="nb">cp </span>src dst <span class="o">&amp;&amp;</span> <span class="nb">mv </span>old new
<span class="nb">rm</span> <span class="nt">-i</span> file           <span class="c"># interactive delete (safer while learning)</span>
less file            <span class="c"># paginated view, quit with q</span>
<span class="nb">head</span> <span class="nt">-n</span> 20 file
<span class="nb">tail</span> <span class="nt">-f</span> log.txt      <span class="c"># follow a growing log</span>
</code></pre></div></div>

<p>Find and search:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">grep</span> <span class="nt">-n</span> <span class="s2">"ERROR"</span> app.log
<span class="nb">grep</span> <span class="nt">-R</span> <span class="s2">"TODO"</span> <span class="nb">.</span> <span class="nt">--include</span><span class="o">=</span><span class="s2">"*.py"</span>
find <span class="nb">.</span> <span class="nt">-name</span> <span class="s2">"*.csv"</span>
</code></pre></div></div>

<p>Two habits worth building immediately: use <code class="language-plaintext highlighter-rouge">Tab</code> for completion (fewer typos, faster) and <code class="language-plaintext highlighter-rouge">Ctrl+R</code> to search command history instead of retyping. <code class="language-plaintext highlighter-rouge">man &lt;command&gt;</code> or <code class="language-plaintext highlighter-rouge">&lt;command&gt; --help</code> beats guessing flags from memory.</p>

<h2 id="pipes-redirection-and-exit-codes">Pipes, redirection, and exit codes</h2>

<p>Programs read stdin and write stdout; chaining them is the entire philosophy of the shell:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat </span>access.log | <span class="nb">grep</span> <span class="s2">" 500 "</span> | <span class="nb">awk</span> <span class="s1">'{print $1}'</span> | <span class="nb">sort</span> | <span class="nb">uniq</span> <span class="nt">-c</span> | <span class="nb">sort</span> <span class="nt">-nr</span> | <span class="nb">head</span>
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Operator</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">\|</code></td>
      <td>pipe stdout → next command’s stdin</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&gt;</code></td>
      <td>overwrite file</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&gt;&gt;</code></td>
      <td>append</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">2&gt;</code></td>
      <td>redirect stderr</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">&amp;&amp;</code></td>
      <td>run next command only if previous succeeded</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">;</code></td>
      <td>run next command regardless</td>
    </tr>
  </tbody>
</table>

<p>Every command leaves an <strong>exit code</strong> in <code class="language-plaintext highlighter-rouge">$?</code> — <code class="language-plaintext highlighter-rouge">0</code> means success, anything else means failure. Scripts that ignore this silently continue after real errors.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">grep</span> <span class="s2">"ERROR"</span> app.log
<span class="nb">echo</span> <span class="nv">$?</span>              <span class="c"># 0 if found, 1 if not found, 2 if app.log doesn't exist</span>
</code></pre></div></div>

<h2 id="paths-permissions-and-the-environment">Paths, permissions, and the environment</h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">.</code> current dir, <code class="language-plaintext highlighter-rouge">..</code> parent, <code class="language-plaintext highlighter-rouge">~</code> home</li>
  <li><code class="language-plaintext highlighter-rouge">chmod +x script.sh &amp;&amp; ./script.sh</code> — the executable bit, not the file extension, controls whether it runs</li>
  <li><code class="language-plaintext highlighter-rouge">chmod 644 file</code> (owner read/write, everyone else read) vs <code class="language-plaintext highlighter-rouge">chmod 600</code> (owner-only) — numeric modes are <code class="language-plaintext highlighter-rouge">owner-group-other</code>, each digit a sum of read(4)+write(2)+execute(1)</li>
  <li><code class="language-plaintext highlighter-rouge">echo $PATH</code> — the ordered list of directories the shell searches for commands; <code class="language-plaintext highlighter-rouge">which python3</code> tells you exactly which binary will run, which matters when you have both a system Python and a venv Python</li>
</ul>

<h2 id="mini-project-build-a-log-analysis-pipeline">Mini project: build a log analysis pipeline</h2>

<p>Generate a synthetic access log with real (pseudo-random) variation, then answer three questions about it with pipelines — no sample output is given here on purpose; you run it and read your own numbers.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for </span>i <span class="k">in</span> <span class="si">$(</span><span class="nb">seq </span>1 40<span class="si">)</span><span class="p">;</span> <span class="k">do
  </span><span class="nv">ip</span><span class="o">=</span><span class="s2">"10.0.0.</span><span class="k">$((</span> RANDOM <span class="o">%</span> <span class="m">5</span> <span class="o">+</span> <span class="m">1</span> <span class="k">))</span><span class="s2">"</span>
  <span class="k">if</span> <span class="o">((</span> RANDOM % 5 <span class="o">==</span> 0 <span class="o">))</span><span class="p">;</span> <span class="k">then </span><span class="nv">status</span><span class="o">=</span>500<span class="p">;</span> <span class="k">else </span><span class="nv">status</span><span class="o">=</span>200<span class="p">;</span> <span class="k">fi
  </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$ip</span><span class="s2"> GET /page</span><span class="k">$((</span> RANDOM <span class="o">%</span> <span class="m">4</span> <span class="k">))</span><span class="s2"> </span><span class="nv">$status</span><span class="s2">"</span>
<span class="k">done</span> <span class="o">&gt;</span> toy.log

<span class="nb">wc</span> <span class="nt">-l</span> toy.log                                                   <span class="c"># should print 40</span>
</code></pre></div></div>

<p>Now answer three questions:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1. Which IP hit the server most?</span>
<span class="nb">awk</span> <span class="s1">'{print $1}'</span> toy.log | <span class="nb">sort</span> | <span class="nb">uniq</span> <span class="nt">-c</span> | <span class="nb">sort</span> <span class="nt">-nr</span> | <span class="nb">head</span> <span class="nt">-5</span>

<span class="c"># 2. What's the status code breakdown?</span>
<span class="nb">awk</span> <span class="s1">'{print $3}'</span> toy.log | <span class="nb">sort</span> | <span class="nb">uniq</span> <span class="nt">-c</span>

<span class="c"># 3. How many 500s happened total?</span>
<span class="nb">grep</span> <span class="nt">-c</span> <span class="s2">" 500$"</span> toy.log
</code></pre></div></div>

<p>Re-run the generator loop and the pipelines again — because it uses <code class="language-plaintext highlighter-rouge">$RANDOM</code>, your numbers will differ each time, which is the point: you’re checking that the <em>pipeline logic</em> is correct, not memorizing one output. Cross-check by hand: add up the counts from question 2 and confirm they sum to 40.</p>

<h2 id="common-footguns">Common footguns</h2>

<ul>
  <li><strong>Unquoted variables</strong> — <code class="language-plaintext highlighter-rouge">rm -rf $DIR/*</code> silently becomes <code class="language-plaintext highlighter-rouge">rm -rf /*</code> if <code class="language-plaintext highlighter-rouge">$DIR</code> is empty or unset. Always quote: <code class="language-plaintext highlighter-rouge">rm -rf "$DIR"/*</code>, and set <code class="language-plaintext highlighter-rouge">set -u</code> in scripts to error on unset variables.</li>
  <li><strong>Parsing <code class="language-plaintext highlighter-rouge">ls</code> output</strong> — filenames with spaces or newlines break naive <code class="language-plaintext highlighter-rouge">for f in $(ls)</code> loops. Use <code class="language-plaintext highlighter-rouge">find . -print0 | xargs -0</code> or a glob (<code class="language-plaintext highlighter-rouge">for f in *.csv</code>) instead.</li>
  <li><strong>Forgetting <code class="language-plaintext highlighter-rouge">set -e</code></strong> — without it, a failing command in a script doesn’t stop the script; later commands run against a broken state.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">source script.sh</code> vs <code class="language-plaintext highlighter-rouge">./script.sh</code></strong> — sourcing runs in your <em>current</em> shell (env var changes persist); executing runs in a subshell (they don’t). Mixing these up is why “but I exported it!” doesn’t always work.</li>
  <li><strong>Trusting PATH order blindly</strong> — <code class="language-plaintext highlighter-rouge">which python3</code> before debugging “wrong version” complaints; a stale <code class="language-plaintext highlighter-rouge">/usr/local/bin</code> entry ahead of your venv is a classic trap.</li>
</ul>

<h2 id="you-know-youre-done-when">You know you’re done when…</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">toy.log</code> contains exactly 40 lines after the generator loop</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />All three analysis pipelines run with no errors and the status-code counts sum to 40</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can explain why <code class="language-plaintext highlighter-rouge">rm -rf "$DIR"/*</code> (quoted) is safer than the unquoted version</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">echo $?</code> immediately after a command tells you pass/fail without re-running anything</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">which &lt;command&gt;</code> points to the binary you actually expect</li>
</ul>

<h2 id="next">Next</h2>

<p><a href="/2026/07/10/getting-started-with-docker/">Getting Started with Docker</a> packages the environment you just learned to navigate. <a href="/2026/07/10/getting-started-with-git/">Getting Started with Git</a> version-controls the scripts you write in the shell.</p>]]></content><author><name></name></author><category term="linux" /><category term="shell" /><category term="getting-started" /><summary type="html"><![CDATA[pwd, pipes, and permissions — then generate a fake access log and build a real analysis pipeline against it, footguns included.]]></summary></entry><entry><title type="html">Getting Started with Python (the useful parts)</title><link href="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-python/" rel="alternate" type="text/html" title="Getting Started with Python (the useful parts)" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/getting-started-with-python</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-python/"><![CDATA[<p>Python is the glue language of modern AI, but you do not need every language feature to be productive. You need a clean environment, a handful of data structures, and the habit of writing small scripts that touch real files and take real arguments. This post gets you there with one tool you build, run, and extend yourself — not a wall of syntax you’ll forget by Friday.</p>

<h2 id="install-and-isolate">Install and isolate</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 <span class="nt">--version</span>          <span class="c"># aim for 3.10+</span>
python3 <span class="nt">-m</span> venv .venv
<span class="nb">source</span> .venv/bin/activate  <span class="c"># Windows (PowerShell): .venv\Scripts\Activate.ps1</span>
python <span class="nt">-c</span> <span class="s2">"import sys; print(sys.executable)"</span>   <span class="c"># confirm it points inside .venv</span>
pip <span class="nb">install</span> <span class="nt">--upgrade</span> pip
</code></pre></div></div>

<p>Two things trip up almost everyone new to Python:</p>

<ul>
  <li>On Ubuntu/Debian the command is <code class="language-plaintext highlighter-rouge">python3</code>, not <code class="language-plaintext highlighter-rouge">python</code>, until you activate a venv (which usually symlinks <code class="language-plaintext highlighter-rouge">python</code> for you). If <code class="language-plaintext highlighter-rouge">python</code> is “not found,” check that first.</li>
  <li><code class="language-plaintext highlighter-rouge">python -c "import sys; print(sys.executable)"</code> is your sanity check. If it prints a system path instead of something ending in <code class="language-plaintext highlighter-rouge">.venv/bin/python</code>, your venv isn’t active — and any <code class="language-plaintext highlighter-rouge">pip install</code> you run next pollutes the wrong environment.</li>
</ul>

<p>One project → one virtualenv. Never <code class="language-plaintext highlighter-rouge">pip install</code> into the system Python for learning work; it turns into unremovable clutter within a month.</p>

<h2 id="core-ideas-in-one-screen">Core ideas in one screen</h2>

<table>
  <thead>
    <tr>
      <th>Idea</th>
      <th>Why it matters</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Everything is an object</td>
      <td><code class="language-plaintext highlighter-rouge">type(x)</code>, <code class="language-plaintext highlighter-rouge">dir(x)</code> teach you the language</td>
    </tr>
    <tr>
      <td>Lists / dicts / sets</td>
      <td>90% of day-one data work</td>
    </tr>
    <tr>
      <td>Functions are first-class</td>
      <td>Pass them, return them, use them in <code class="language-plaintext highlighter-rouge">map</code>/<code class="language-plaintext highlighter-rouge">filter</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">with</code> for resources</td>
      <td>Files and connections close correctly, even on error</td>
    </tr>
    <tr>
      <td>Comprehensions</td>
      <td>Compact transforms without a <code class="language-plaintext highlighter-rouge">for</code>-loop wrapper</td>
    </tr>
  </tbody>
</table>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>

<span class="c1"># list comprehension: transform + filter in one line
</span><span class="n">lengths</span> <span class="o">=</span> <span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">w</span><span class="p">)</span> <span class="k">for</span> <span class="n">w</span> <span class="ow">in</span> <span class="s">"a longer sentence here"</span><span class="p">.</span><span class="n">split</span><span class="p">()</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">w</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">3</span><span class="p">]</span>

<span class="c1"># dict comprehension: build lookups fast
</span><span class="n">by_len</span> <span class="o">=</span> <span class="p">{</span><span class="n">w</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">w</span><span class="p">)</span> <span class="k">for</span> <span class="n">w</span> <span class="ow">in</span> <span class="p">[</span><span class="s">"cat"</span><span class="p">,</span> <span class="s">"elephant"</span><span class="p">,</span> <span class="s">"ox"</span><span class="p">]}</span>

<span class="c1"># `with` guarantees the file handle closes even if read() raises
</span><span class="n">text</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="s">"notes.txt"</span><span class="p">).</span><span class="n">read_text</span><span class="p">(</span><span class="n">encoding</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">)</span> <span class="k">if</span> <span class="n">Path</span><span class="p">(</span><span class="s">"notes.txt"</span><span class="p">).</span><span class="n">exists</span><span class="p">()</span> <span class="k">else</span> <span class="s">""</span>
</code></pre></div></div>

<h2 id="mini-project-a-real-cli-tool-not-a-toy-snippet">Mini project: a real CLI tool, not a toy snippet</h2>

<p>Most “getting started” posts show you a script you run once. Build a small command-line tool instead — it forces you to touch arguments, files, and error paths, which is where Python actually gets used.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#!/usr/bin/env python3
</span><span class="s">"""wordcount.py - rank word frequency across one or more text files."""</span>
<span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">Counter</span>
<span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span>

<span class="n">STOPWORDS</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">"the"</span><span class="p">,</span> <span class="s">"a"</span><span class="p">,</span> <span class="s">"an"</span><span class="p">,</span> <span class="s">"and"</span><span class="p">,</span> <span class="s">"or"</span><span class="p">,</span> <span class="s">"but"</span><span class="p">,</span> <span class="s">"is"</span><span class="p">,</span> <span class="s">"are"</span><span class="p">,</span> <span class="s">"was"</span><span class="p">,</span> <span class="s">"were"</span><span class="p">,</span>
    <span class="s">"to"</span><span class="p">,</span> <span class="s">"of"</span><span class="p">,</span> <span class="s">"in"</span><span class="p">,</span> <span class="s">"on"</span><span class="p">,</span> <span class="s">"for"</span><span class="p">,</span> <span class="s">"it"</span><span class="p">,</span> <span class="s">"this"</span><span class="p">,</span> <span class="s">"that"</span><span class="p">,</span>
<span class="p">}</span>


<span class="k">def</span> <span class="nf">read_words</span><span class="p">(</span><span class="n">path</span><span class="p">:</span> <span class="n">Path</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">list</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
    <span class="n">text</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="n">read_text</span><span class="p">(</span><span class="n">encoding</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">).</span><span class="n">lower</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s">"[a-z']+"</span><span class="p">,</span> <span class="n">text</span><span class="p">)</span>


<span class="k">def</span> <span class="nf">main</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s">"Count word frequency in text files."</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"files"</span><span class="p">,</span> <span class="n">nargs</span><span class="o">=</span><span class="s">"+"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="n">Path</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"one or more text files"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"-n"</span><span class="p">,</span> <span class="s">"--top"</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"how many words to show"</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">"--no-stopwords"</span><span class="p">,</span> <span class="n">action</span><span class="o">=</span><span class="s">"store_true"</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">"drop common stopwords"</span><span class="p">)</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">()</span>

    <span class="n">counts</span><span class="p">:</span> <span class="n">Counter</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">()</span>
    <span class="k">for</span> <span class="n">path</span> <span class="ow">in</span> <span class="n">args</span><span class="p">.</span><span class="n">files</span><span class="p">:</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">():</span>
            <span class="k">raise</span> <span class="nb">SystemExit</span><span class="p">(</span><span class="sa">f</span><span class="s">"error: </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s"> does not exist"</span><span class="p">)</span>
        <span class="n">counts</span><span class="p">.</span><span class="n">update</span><span class="p">(</span><span class="n">read_words</span><span class="p">(</span><span class="n">path</span><span class="p">))</span>

    <span class="k">if</span> <span class="n">args</span><span class="p">.</span><span class="n">no_stopwords</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">STOPWORDS</span><span class="p">:</span>
            <span class="n">counts</span><span class="p">.</span><span class="n">pop</span><span class="p">(</span><span class="n">word</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">word</span><span class="p">,</span> <span class="n">n</span> <span class="ow">in</span> <span class="n">counts</span><span class="p">.</span><span class="n">most_common</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">top</span><span class="p">):</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">n</span><span class="si">:</span><span class="mi">5</span><span class="n">d</span><span class="si">}</span><span class="s">  </span><span class="si">{</span><span class="n">word</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
    <span class="n">main</span><span class="p">()</span>
</code></pre></div></div>

<p>Run it end to end:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">printf</span> <span class="s2">"the quick brown fox jumps over the lazy dog. the dog barks at the fox.</span><span class="se">\n</span><span class="s2">"</span> <span class="o">&gt;</span> notes.txt
python wordcount.py notes.txt
python wordcount.py notes.txt <span class="nt">--no-stopwords</span> <span class="nt">-n</span> 5
python wordcount.py missing.txt      <span class="c"># exercises the error path — should exit cleanly, not traceback</span>
</code></pre></div></div>

<p>The first run shows <code class="language-plaintext highlighter-rouge">the</code> at the top with count 4. The second, with stopwords stripped, promotes <code class="language-plaintext highlighter-rouge">dog</code> and <code class="language-plaintext highlighter-rouge">fox</code> (count 2 each) to the top. The third should print your <code class="language-plaintext highlighter-rouge">SystemExit</code> message and exit with a non-zero code — try <code class="language-plaintext highlighter-rouge">echo $?</code> right after to confirm.</p>

<p>Extend it yourself: add a <code class="language-plaintext highlighter-rouge">--min-length</code> flag, or read from stdin when no files are given (<code class="language-plaintext highlighter-rouge">if not sys.stdin.isatty(): ...</code>).</p>

<h2 id="common-footguns">Common footguns</h2>

<ul>
  <li><strong>Mutable default arguments</strong> — <code class="language-plaintext highlighter-rouge">def f(x=[])</code> reuses the <em>same</em> list across every call. Use <code class="language-plaintext highlighter-rouge">def f(x=None): x = x or []</code>.</li>
  <li><strong>Bare <code class="language-plaintext highlighter-rouge">except:</code></strong> — swallows <code class="language-plaintext highlighter-rouge">KeyboardInterrupt</code> and typos alike. Catch the specific exception you can actually handle.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">range(len(x))</code> instead of <code class="language-plaintext highlighter-rouge">enumerate(x)</code></strong> — works, but hides intent and invites off-by-one bugs.</li>
  <li><strong>Comparing floats with <code class="language-plaintext highlighter-rouge">==</code></strong> — <code class="language-plaintext highlighter-rouge">0.1 + 0.2 == 0.3</code> is <code class="language-plaintext highlighter-rouge">False</code>. Use <code class="language-plaintext highlighter-rouge">math.isclose()</code>.</li>
  <li><strong>Forgetting <code class="language-plaintext highlighter-rouge">.lower()</code> before comparing strings</strong> — silent case-mismatch bugs that “work on my test data.”</li>
  <li><strong>Mixing tabs and spaces</strong> — Python 3 raises <code class="language-plaintext highlighter-rouge">TabError</code>; configure your editor to show whitespace and insert spaces only.</li>
</ul>

<h2 id="you-know-youre-done-when">You know you’re done when…</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">python -c "import sys; print(sys.executable)"</code> prints a path inside <code class="language-plaintext highlighter-rouge">.venv</code>, not your system Python</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">python wordcount.py notes.txt</code> runs with no traceback and prints ranked counts</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">--no-stopwords</code> visibly changes which words rank highest</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">python wordcount.py missing.txt</code> fails with your error message, not a raw <code class="language-plaintext highlighter-rouge">FileNotFoundError</code> traceback</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can explain, without looking it up, why <code class="language-plaintext highlighter-rouge">def f(x=[])</code> is a bug</li>
</ul>

<h2 id="next">Next</h2>

<ul>
  <li><a href="/courses/python-10-days/">Python in 10 Days</a> — structured sprint</li>
  <li><a href="/2026/07/10/getting-started-with-pytorch/">Getting Started with PyTorch</a> — tensors next</li>
  <li><a href="/2026/07/10/getting-started-with-fastapi/">Getting Started with FastAPI</a> — wrap scripts like this one behind HTTP</li>
  <li><a href="/courses/llm-mastery/">LLM Mastery</a> — when you want language models from first principles</li>
</ul>]]></content><author><name></name></author><category term="python" /><category term="getting-started" /><summary type="html"><![CDATA[Install a clean venv, learn the data structures that matter, then build a real argparse CLI tool end to end — the Python you need before ML and LLMs.]]></summary></entry><entry><title type="html">Getting Started with PyTorch for LLMs</title><link href="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-pytorch/" rel="alternate" type="text/html" title="Getting Started with PyTorch for LLMs" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/getting-started-with-pytorch</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-pytorch/"><![CDATA[<p>PyTorch is the default dialect for research-flavored deep learning. If you can shape tensors, write a <code class="language-plaintext highlighter-rouge">forward</code>, and run a training step, you can follow almost any LLM tutorial. This post trains an actual (tiny) classifier to convergence, so you have numbers to check rather than code to take on faith.</p>

<h2 id="install">Install</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 <span class="nt">-m</span> venv .venv <span class="o">&amp;&amp;</span> <span class="nb">source</span> .venv/bin/activate
pip <span class="nb">install </span>torch    <span class="c"># see pytorch.org for a CUDA-specific build if you have an NVIDIA GPU</span>
python <span class="nt">-c</span> <span class="s2">"import torch; print(torch.__version__, torch.cuda.is_available())"</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">torch.cuda.is_available()</code> prints <code class="language-plaintext highlighter-rouge">False</code> on most laptops — that’s expected, not broken. Everything below runs fine on CPU; it’s small on purpose.</p>

<h2 id="tensors">Tensors</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">torch</span>

<span class="n">x</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">randn</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>       <span class="c1"># shape (2, 3)
</span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span> <span class="o">@</span> <span class="n">x</span><span class="p">.</span><span class="n">T</span>                 <span class="c1"># (2, 2)
</span><span class="k">print</span><span class="p">(</span><span class="n">y</span><span class="p">.</span><span class="n">shape</span><span class="p">,</span> <span class="n">y</span><span class="p">.</span><span class="n">dtype</span><span class="p">)</span>

<span class="n">a</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">tensor</span><span class="p">([</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">,</span> <span class="mf">3.0</span><span class="p">],</span> <span class="n">requires_grad</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">loss</span> <span class="o">=</span> <span class="p">(</span><span class="n">a</span> <span class="o">**</span> <span class="mi">2</span><span class="p">).</span><span class="nb">sum</span><span class="p">()</span>
<span class="n">loss</span><span class="p">.</span><span class="n">backward</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">grad</span><span class="p">)</span>                <span class="c1"># d(loss)/da == 2*a == [2., 4., 6.]
</span></code></pre></div></div>

<p>Shapes are a language. Write them in comments until they’re muscle memory: <code class="language-plaintext highlighter-rouge">(B, T, C)</code> for batch, time, channels. A <code class="language-plaintext highlighter-rouge">RuntimeError: shapes cannot be multiplied</code> is PyTorch telling you exactly where your mental model diverged from reality — read it, don’t guess.</p>

<h2 id="nnmodule-in-one-page">nn.Module in one page</h2>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">torch.nn</span> <span class="k">as</span> <span class="n">nn</span>

<span class="k">class</span> <span class="nc">TinyMLP</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Module</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">d_in</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">d_hid</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">d_out</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">().</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">net</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Sequential</span><span class="p">(</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">d_in</span><span class="p">,</span> <span class="n">d_hid</span><span class="p">),</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(),</span>
            <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="n">d_hid</span><span class="p">,</span> <span class="n">d_out</span><span class="p">),</span>
        <span class="p">)</span>

    <span class="k">def</span> <span class="nf">forward</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span><span class="p">.</span><span class="n">net</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="mini-project-train-it-to-convergence-and-check-the-numbers">Mini project: train it to convergence and check the numbers</h2>

<p>Two Gaussian blobs, one small network, fifty epochs. This is small enough to run in seconds on a CPU and large enough to show a real loss curve.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">torch</span>
<span class="kn">import</span> <span class="nn">torch.nn</span> <span class="k">as</span> <span class="n">nn</span>
<span class="kn">import</span> <span class="nn">torch.optim</span> <span class="k">as</span> <span class="n">optim</span>

<span class="n">torch</span><span class="p">.</span><span class="n">manual_seed</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>

<span class="n">n</span> <span class="o">=</span> <span class="mi">200</span>
<span class="n">x0</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">randn</span><span class="p">(</span><span class="n">n</span> <span class="o">//</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">torch</span><span class="p">.</span><span class="n">tensor</span><span class="p">([</span><span class="o">-</span><span class="mf">2.0</span><span class="p">,</span> <span class="o">-</span><span class="mf">2.0</span><span class="p">])</span>   <span class="c1"># class 0, centered at (-2,-2)
</span><span class="n">x1</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">randn</span><span class="p">(</span><span class="n">n</span> <span class="o">//</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">+</span> <span class="n">torch</span><span class="p">.</span><span class="n">tensor</span><span class="p">([</span><span class="mf">2.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">])</span>      <span class="c1"># class 1, centered at (2,2)
</span><span class="n">X</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">cat</span><span class="p">([</span><span class="n">x0</span><span class="p">,</span> <span class="n">x1</span><span class="p">])</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">torch</span><span class="p">.</span><span class="n">cat</span><span class="p">([</span><span class="n">torch</span><span class="p">.</span><span class="n">zeros</span><span class="p">(</span><span class="n">n</span> <span class="o">//</span> <span class="mi">2</span><span class="p">),</span> <span class="n">torch</span><span class="p">.</span><span class="n">ones</span><span class="p">(</span><span class="n">n</span> <span class="o">//</span> <span class="mi">2</span><span class="p">)]).</span><span class="nb">long</span><span class="p">()</span>   <span class="c1"># CrossEntropyLoss needs Long targets
</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">Sequential</span><span class="p">(</span><span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">),</span> <span class="n">nn</span><span class="p">.</span><span class="n">ReLU</span><span class="p">(),</span> <span class="n">nn</span><span class="p">.</span><span class="n">Linear</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span>
<span class="n">opt</span> <span class="o">=</span> <span class="n">optim</span><span class="p">.</span><span class="n">AdamW</span><span class="p">(</span><span class="n">model</span><span class="p">.</span><span class="n">parameters</span><span class="p">(),</span> <span class="n">lr</span><span class="o">=</span><span class="mf">0.05</span><span class="p">)</span>

<span class="k">for</span> <span class="n">epoch</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">50</span><span class="p">):</span>
    <span class="n">logits</span> <span class="o">=</span> <span class="n">model</span><span class="p">(</span><span class="n">X</span><span class="p">)</span>
    <span class="n">loss</span> <span class="o">=</span> <span class="n">nn</span><span class="p">.</span><span class="n">functional</span><span class="p">.</span><span class="n">cross_entropy</span><span class="p">(</span><span class="n">logits</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
    <span class="n">opt</span><span class="p">.</span><span class="n">zero_grad</span><span class="p">(</span><span class="n">set_to_none</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
    <span class="n">loss</span><span class="p">.</span><span class="n">backward</span><span class="p">()</span>
    <span class="n">opt</span><span class="p">.</span><span class="n">step</span><span class="p">()</span>
    <span class="k">if</span> <span class="n">epoch</span> <span class="o">%</span> <span class="mi">10</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">acc</span> <span class="o">=</span> <span class="p">(</span><span class="n">logits</span><span class="p">.</span><span class="n">argmax</span><span class="p">(</span><span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="n">y</span><span class="p">).</span><span class="nb">float</span><span class="p">().</span><span class="n">mean</span><span class="p">()</span>
        <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"epoch </span><span class="si">{</span><span class="n">epoch</span><span class="si">:</span><span class="mi">3</span><span class="n">d</span><span class="si">}</span><span class="s">  loss </span><span class="si">{</span><span class="n">loss</span><span class="p">.</span><span class="n">item</span><span class="p">()</span><span class="si">:</span><span class="p">.</span><span class="mi">4</span><span class="n">f</span><span class="si">}</span><span class="s">  acc </span><span class="si">{</span><span class="n">acc</span><span class="p">.</span><span class="n">item</span><span class="p">()</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>

<span class="k">with</span> <span class="n">torch</span><span class="p">.</span><span class="n">no_grad</span><span class="p">():</span>
    <span class="n">final_acc</span> <span class="o">=</span> <span class="p">(</span><span class="n">model</span><span class="p">(</span><span class="n">X</span><span class="p">).</span><span class="n">argmax</span><span class="p">(</span><span class="n">dim</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="n">y</span><span class="p">).</span><span class="nb">float</span><span class="p">().</span><span class="n">mean</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"final accuracy: </span><span class="si">{</span><span class="n">final_acc</span><span class="p">.</span><span class="n">item</span><span class="p">()</span><span class="si">:</span><span class="p">.</span><span class="mi">2</span><span class="n">f</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Run it and check two things concretely: the printed <code class="language-plaintext highlighter-rouge">loss</code> should trend down across epochs (not flat, not <code class="language-plaintext highlighter-rouge">nan</code>), and <code class="language-plaintext highlighter-rouge">final accuracy</code> should land at or near <code class="language-plaintext highlighter-rouge">1.00</code> — the two blobs are far enough apart (centers 4 units apart, unit-variance noise) that a tiny two-layer network separates them almost perfectly. If accuracy stays near <code class="language-plaintext highlighter-rouge">0.50</code>, something upstream is broken — gradients not flowing, wrong loss, or a shape mismatch silently broadcasting incorrectly — not “the model needs more epochs.”</p>

<p>That loop — forward, loss, <code class="language-plaintext highlighter-rouge">zero_grad</code>, <code class="language-plaintext highlighter-rouge">backward</code>, <code class="language-plaintext highlighter-rouge">step</code> — is the heartbeat of every LLM training run, from this ten-line example to a multi-billion parameter model.</p>

<h2 id="habits-for-llm-work">Habits for LLM work</h2>

<ul>
  <li>Use <code class="language-plaintext highlighter-rouge">torch.no_grad()</code> or <code class="language-plaintext highlighter-rouge">torch.inference_mode()</code> when evaluating — it skips building the autograd graph, saving memory and time for computations you won’t backpropagate through (used above in the final accuracy check).</li>
  <li>Move tensors with <code class="language-plaintext highlighter-rouge">.to(device)</code> once, near the top; keep the model and every batch on the <em>same</em> device — mismatches raise <code class="language-plaintext highlighter-rouge">RuntimeError: Expected all tensors to be on the same device</code>.</li>
  <li>Log <strong>shapes</strong> when debugging, not just values — <code class="language-plaintext highlighter-rouge">print(x.shape)</code> finds broadcasting bugs faster than staring at numbers.</li>
  <li>Start CPU-small, like this example; add GPU only once the toy version is verified correct. Debugging correctness and debugging performance are different problems — don’t mix them.</li>
</ul>

<h2 id="common-footguns">Common footguns</h2>

<ul>
  <li><strong>Forgetting <code class="language-plaintext highlighter-rouge">zero_grad()</code></strong> — gradients <em>accumulate</em> by default across <code class="language-plaintext highlighter-rouge">.backward()</code> calls; skip clearing them and your updates silently compound garbage from previous steps.</li>
  <li><strong>Wrong target dtype</strong> — <code class="language-plaintext highlighter-rouge">nn.functional.cross_entropy</code> requires <code class="language-plaintext highlighter-rouge">Long</code> (integer class index) targets, not <code class="language-plaintext highlighter-rouge">Float</code>. Passing floats raises a cryptic error or silently misbehaves depending on the PyTorch version.</li>
  <li><strong>Missing <code class="language-plaintext highlighter-rouge">model.eval()</code> / <code class="language-plaintext highlighter-rouge">torch.no_grad()</code> during evaluation</strong> — irrelevant for this tiny model (no dropout or batchnorm), but essential once you add either; forgetting <code class="language-plaintext highlighter-rouge">eval()</code> leaves dropout active during “evaluation,” giving noisy, non-reproducible metrics.</li>
  <li><strong>Device mismatches</strong> — moving the model to GPU but leaving a batch on CPU (or vice versa) is one of the most common real-world PyTorch errors.</li>
  <li><strong>Reusing a model/optimizer across notebook cells without resetting</strong> — re-running a training cell without re-initializing <code class="language-plaintext highlighter-rouge">model</code>/<code class="language-plaintext highlighter-rouge">opt</code> continues training from wherever it left off, which is easy to mistake for “starting fresh.”</li>
</ul>

<h2 id="you-know-youre-done-when">You know you’re done when…</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />The printed loss trends downward across the 50 epochs, never flat or <code class="language-plaintext highlighter-rouge">nan</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Final accuracy prints at or near <code class="language-plaintext highlighter-rouge">1.00</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">torch.cuda.is_available()</code> correctly reflects your actual hardware, so you’re not debugging “slow training” on a CPU-only box expecting GPU speed</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can point to the exact three lines where gradients are cleared, computed, and applied</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can explain why <code class="language-plaintext highlighter-rouge">y</code> is <code class="language-plaintext highlighter-rouge">.long()</code>, not <code class="language-plaintext highlighter-rouge">.float()</code>, for <code class="language-plaintext highlighter-rouge">cross_entropy</code></li>
</ul>

<h2 id="next">Next</h2>

<p><a href="/courses/llm-mastery/">LLM Mastery</a> — especially articles 11–27 (autograd through GPT internals). Pair with <a href="/2026/07/10/getting-started-with-python/">Getting Started with Python</a> if the syntax still feels new.</p>]]></content><author><name></name></author><category term="pytorch" /><category term="llm" /><category term="getting-started" /><summary type="html"><![CDATA[Tensors, autograd, nn.Module, and a full training loop on real synthetic data — watch loss actually drop and accuracy actually climb.]]></summary></entry><entry><title type="html">Getting Started with SQL</title><link href="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-sql/" rel="alternate" type="text/html" title="Getting Started with SQL" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/getting-started-with-sql</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/getting-started-with-sql/"><![CDATA[<p>SQL is how you ask structured questions of tables. Whether you use Postgres, SQLite, or a warehouse, the core verbs stay the same. This post has you build a tiny blog schema, load real sample rows, and run queries whose output you can verify by counting the <code class="language-plaintext highlighter-rouge">INSERT</code>s yourself — no trust required.</p>

<h2 id="core-verbs">Core verbs</h2>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">id</span><span class="p">,</span> <span class="n">title</span>
<span class="k">FROM</span> <span class="n">posts</span>
<span class="k">WHERE</span> <span class="n">published</span> <span class="o">=</span> <span class="k">TRUE</span>
<span class="k">ORDER</span> <span class="k">BY</span> <span class="n">created_at</span> <span class="k">DESC</span>
<span class="k">LIMIT</span> <span class="mi">10</span><span class="p">;</span>
</code></pre></div></div>

<p>Aggregations:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">author_id</span><span class="p">,</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">AS</span> <span class="n">n_posts</span>
<span class="k">FROM</span> <span class="n">posts</span>
<span class="k">GROUP</span> <span class="k">BY</span> <span class="n">author_id</span>
<span class="k">HAVING</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">3</span>
<span class="k">ORDER</span> <span class="k">BY</span> <span class="n">n_posts</span> <span class="k">DESC</span><span class="p">;</span>
</code></pre></div></div>

<p>Joins — always explicit, never a bare comma:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">u</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="n">p</span><span class="p">.</span><span class="n">title</span>
<span class="k">FROM</span> <span class="n">users</span> <span class="n">u</span>
<span class="k">JOIN</span> <span class="n">posts</span> <span class="n">p</span> <span class="k">ON</span> <span class="n">p</span><span class="p">.</span><span class="n">author_id</span> <span class="o">=</span> <span class="n">u</span><span class="p">.</span><span class="n">id</span>
<span class="k">WHERE</span> <span class="n">u</span><span class="p">.</span><span class="n">active</span> <span class="o">=</span> <span class="k">TRUE</span><span class="p">;</span>
</code></pre></div></div>

<p>Mutations (use carefully in production):</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">posts</span> <span class="p">(</span><span class="n">author_id</span><span class="p">,</span> <span class="n">title</span><span class="p">)</span> <span class="k">VALUES</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'Hello'</span><span class="p">);</span>
<span class="k">UPDATE</span> <span class="n">posts</span> <span class="k">SET</span> <span class="n">published</span> <span class="o">=</span> <span class="k">TRUE</span> <span class="k">WHERE</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">42</span><span class="p">;</span>
<span class="k">DELETE</span> <span class="k">FROM</span> <span class="n">comments</span> <span class="k">WHERE</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">7</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="mental-model">Mental model</h2>

<ul>
  <li>Tables ≈ spreadsheets with types and constraints.</li>
  <li>Rows ≈ records; columns ≈ fields.</li>
  <li><code class="language-plaintext highlighter-rouge">JOIN</code> stitches tables together on matching keys.</li>
  <li><code class="language-plaintext highlighter-rouge">GROUP BY</code> collapses rows into buckets; <code class="language-plaintext highlighter-rouge">HAVING</code> filters <em>buckets</em>, <code class="language-plaintext highlighter-rouge">WHERE</code> filters <em>rows before</em> grouping.</li>
</ul>

<h2 id="mini-project-a-blog-schema-with-data-you-can-verify">Mini project: a blog schema with data you can verify</h2>

<p>Use SQLite for zero setup — one binary, one file, no server.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sqlite3 blog.db
</code></pre></div></div>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">users</span> <span class="p">(</span>
  <span class="n">id</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
  <span class="n">name</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
  <span class="n">email</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">UNIQUE</span>
<span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">posts</span> <span class="p">(</span>
  <span class="n">id</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
  <span class="n">author_id</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">REFERENCES</span> <span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
  <span class="n">title</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span>
<span class="p">);</span>

<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">comments</span> <span class="p">(</span>
  <span class="n">id</span> <span class="nb">INTEGER</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
  <span class="n">post_id</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">REFERENCES</span> <span class="n">posts</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
  <span class="n">user_id</span> <span class="nb">INTEGER</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">REFERENCES</span> <span class="n">users</span><span class="p">(</span><span class="n">id</span><span class="p">),</span>
  <span class="n">body</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span>
<span class="p">);</span>

<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">users</span> <span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">email</span><span class="p">)</span> <span class="k">VALUES</span>
  <span class="p">(</span><span class="s1">'Asha'</span><span class="p">,</span> <span class="s1">'asha@example.com'</span><span class="p">),</span>
  <span class="p">(</span><span class="s1">'Ravi'</span><span class="p">,</span> <span class="s1">'ravi@example.com'</span><span class="p">),</span>
  <span class="p">(</span><span class="s1">'Meera'</span><span class="p">,</span> <span class="s1">'meera@example.com'</span><span class="p">);</span>

<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">posts</span> <span class="p">(</span><span class="n">author_id</span><span class="p">,</span> <span class="n">title</span><span class="p">)</span> <span class="k">VALUES</span>
  <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'Hello SQL'</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'Joins Explained'</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">'Why Indexes Matter'</span><span class="p">);</span>

<span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">comments</span> <span class="p">(</span><span class="n">post_id</span><span class="p">,</span> <span class="n">user_id</span><span class="p">,</span> <span class="n">body</span><span class="p">)</span> <span class="k">VALUES</span>
  <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="s1">'Great intro!'</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="s1">'Finally makes sense'</span><span class="p">),</span>
  <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="s1">'More examples please'</span><span class="p">);</span>
</code></pre></div></div>

<p>Now run three queries and verify each against the <code class="language-plaintext highlighter-rouge">INSERT</code>s above before trusting the output:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- 1. All comments on post 1, with commenter names</span>
<span class="k">SELECT</span> <span class="k">c</span><span class="p">.</span><span class="n">body</span><span class="p">,</span> <span class="n">u</span><span class="p">.</span><span class="n">name</span> <span class="k">AS</span> <span class="n">commenter</span>
<span class="k">FROM</span> <span class="n">comments</span> <span class="k">c</span>
<span class="k">JOIN</span> <span class="n">users</span> <span class="n">u</span> <span class="k">ON</span> <span class="n">u</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="k">c</span><span class="p">.</span><span class="n">user_id</span>
<span class="k">WHERE</span> <span class="k">c</span><span class="p">.</span><span class="n">post_id</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="c1">-- expect exactly 2 rows: Ravi's and Meera's comments on 'Hello SQL'</span>

<span class="c1">-- 2. Top commenters by comment count</span>
<span class="k">SELECT</span> <span class="n">u</span><span class="p">.</span><span class="n">name</span><span class="p">,</span> <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">AS</span> <span class="n">n_comments</span>
<span class="k">FROM</span> <span class="n">comments</span> <span class="k">c</span>
<span class="k">JOIN</span> <span class="n">users</span> <span class="n">u</span> <span class="k">ON</span> <span class="n">u</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="k">c</span><span class="p">.</span><span class="n">user_id</span>
<span class="k">GROUP</span> <span class="k">BY</span> <span class="n">u</span><span class="p">.</span><span class="n">name</span>
<span class="k">ORDER</span> <span class="k">BY</span> <span class="n">n_comments</span> <span class="k">DESC</span><span class="p">;</span>
<span class="c1">-- expect Meera first with 2 (she commented on posts 1 and 2), Ravi with 1</span>

<span class="c1">-- 3. Posts with zero comments</span>
<span class="k">SELECT</span> <span class="n">p</span><span class="p">.</span><span class="n">title</span>
<span class="k">FROM</span> <span class="n">posts</span> <span class="n">p</span>
<span class="k">LEFT</span> <span class="k">JOIN</span> <span class="n">comments</span> <span class="k">c</span> <span class="k">ON</span> <span class="k">c</span><span class="p">.</span><span class="n">post_id</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">id</span>
<span class="k">WHERE</span> <span class="k">c</span><span class="p">.</span><span class="n">id</span> <span class="k">IS</span> <span class="k">NULL</span><span class="p">;</span>
<span class="c1">-- expect exactly 'Why Indexes Matter' (post 3 has no comments above)</span>
</code></pre></div></div>

<p>If your output doesn’t match the comments in each query, re-count the <code class="language-plaintext highlighter-rouge">INSERT</code> statements — that mismatch is the whole point of the exercise: SQL results are always derivable by hand from small data, so “it looks right” is never good enough.</p>

<h2 id="common-footguns">Common footguns</h2>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">WHERE x = NULL</code> never matches anything</strong> — <code class="language-plaintext highlighter-rouge">NULL</code> isn’t equal to anything, including itself. Use <code class="language-plaintext highlighter-rouge">WHERE x IS NULL</code> / <code class="language-plaintext highlighter-rouge">IS NOT NULL</code>.</li>
  <li><strong>SQLite is lenient about <code class="language-plaintext highlighter-rouge">GROUP BY</code></strong> — it lets you <code class="language-plaintext highlighter-rouge">SELECT</code> a non-aggregated, non-grouped column without erroring (many other databases reject this outright). The value you get back is essentially arbitrary; only select columns that are either grouped or aggregated.</li>
  <li><strong>Missing <code class="language-plaintext highlighter-rouge">ON</code> clause → cartesian join</strong> — <code class="language-plaintext highlighter-rouge">FROM a, b</code> without a join condition returns every combination of rows from both tables, which silently explodes result size on real data.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">LEFT JOIN</code> + <code class="language-plaintext highlighter-rouge">WHERE</code> on the right table’s column (not <code class="language-plaintext highlighter-rouge">IS NULL</code>)</strong> — turns your outer join back into an inner join by accident, because <code class="language-plaintext highlighter-rouge">WHERE comments.body = 'x'</code> filters out the <code class="language-plaintext highlighter-rouge">NULL</code> rows the <code class="language-plaintext highlighter-rouge">LEFT JOIN</code> produced.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">SELECT *</code> in application code</strong> — breaks silently when someone adds or reorders a column; name the columns you actually use.</li>
  <li><strong>No index on a foreign key</strong> — joins on <code class="language-plaintext highlighter-rouge">author_id</code> or <code class="language-plaintext highlighter-rouge">post_id</code> do a full table scan once your tables grow past a few thousand rows; <code class="language-plaintext highlighter-rouge">CREATE INDEX idx_posts_author ON posts(author_id);</code> fixes it.</li>
</ul>

<h2 id="you-know-youre-done-when">You know you’re done when…</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" /><code class="language-plaintext highlighter-rouge">sqlite3 blog.db ".tables"</code> lists <code class="language-plaintext highlighter-rouge">users</code>, <code class="language-plaintext highlighter-rouge">posts</code>, and <code class="language-plaintext highlighter-rouge">comments</code></li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />All three exercise queries return exactly the row counts predicted in the comments above</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can explain why <code class="language-plaintext highlighter-rouge">WHERE comments.id IS NULL</code> after a <code class="language-plaintext highlighter-rouge">LEFT JOIN</code> finds posts with no comments — and why <code class="language-plaintext highlighter-rouge">WHERE comments.id = NULL</code> would return nothing at all</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />Every join in your queries uses explicit <code class="language-plaintext highlighter-rouge">JOIN ... ON</code>, never a bare comma</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can name one column in this schema that would benefit from an index, and why</li>
</ul>

<h2 id="next">Next</h2>

<p>APIs often wrap SQL — see <a href="/2026/07/10/getting-started-with-fastapi/">Getting Started with FastAPI</a>. RAG systems sometimes store metadata in SQL and vectors elsewhere — see <a href="/courses/llm-mastery/40-rag-retrieval/">LLM Mastery art. 40</a>.</p>]]></content><author><name></name></author><category term="sql" /><category term="getting-started" /><summary type="html"><![CDATA[SELECT, JOIN, GROUP BY, and a real blog schema you build in SQLite with sample data — verify every query against rows you can count by hand.]]></summary></entry><entry><title type="html">Hot language hands-on tutorials (TypeScript, Rust, Go, and more)</title><link href="https://shardalearningcenter.github.io/2026/07/10/hot-language-hands-on-tutorials/" rel="alternate" type="text/html" title="Hot language hands-on tutorials (TypeScript, Rust, Go, and more)" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/hot-language-hands-on-tutorials</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/hot-language-hands-on-tutorials/"><![CDATA[<p>We published a full <strong><a href="/courses/languages/">Languages hub</a></strong> with hands-on, day-by-day sprints — not reference manuals you skim once and forget, but courses built around a task you complete each day.</p>

<h2 id="what-hands-on-actually-means">What “hands-on” actually means</h2>

<p>Every course in the hub follows the same structure: a short code-along that introduces the day’s concept, a concrete task you complete yourself using that concept, and a capstone project at the end that forces you to combine everything from the earlier days. There’s no day that’s just “read about generics” with nothing to run afterward — if a day doesn’t produce code you executed and checked, it’s not in the course.</p>

<p>As an example, <a href="/courses/go-10-days/">Go in 10 Days</a> spends day one on goroutines and channels conceptually, then has you build a small concurrent worker pool that fetches a list of URLs in parallel and reports which ones failed — a task that only “works” if you actually understood the concurrency primitives, not just read about them.</p>

<h2 id="the-full-list">The full list</h2>

<ul>
  <li><a href="/courses/typescript-10-days/">TypeScript</a> — 10 days</li>
  <li><a href="/courses/javascript-10-days/">JavaScript</a> — 10 days</li>
  <li><a href="/courses/rust-10-days/">Rust</a> — 10 days</li>
  <li><a href="/courses/go-10-days/">Go</a> — 10 days</li>
  <li><a href="/courses/java-10-days/">Java</a> — 10 days</li>
  <li><a href="/courses/kotlin-10-days/">Kotlin</a> — 10 days</li>
  <li><a href="/courses/swift-10-days/">Swift</a> — 10 days</li>
  <li><a href="/courses/cpp-10-days/">C++</a> — 10 days</li>
  <li><a href="/courses/csharp-10-days/">C#</a> — 10 days</li>
  <li><a href="/courses/ruby-10-days/">Ruby</a> — 10 days</li>
  <li><a href="/courses/php-10-days/">PHP</a> — 10 days</li>
  <li><a href="/courses/sql-10-days/">SQL</a> — 10 days</li>
  <li><a href="/courses/zig-7-days/">Zig</a> — 7 days</li>
  <li><a href="/courses/bash-7-days/">Bash</a> — 7 days</li>
  <li><a href="/courses/python-10-days/">Python in 10 Days</a> — already on the site</li>
</ul>

<h2 id="how-to-actually-pick-one">How to actually pick one</h2>

<p>Don’t pick based on hype alone — match the language to what you’re trying to build next:</p>

<table>
  <thead>
    <tr>
      <th>If you’re aiming at</th>
      <th>Start with</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Frontend or full-stack web work</td>
      <td>TypeScript, then JavaScript if you skipped it</td>
    </tr>
    <tr>
      <td>Systems, performance-critical services</td>
      <td>Rust or Go</td>
    </tr>
    <tr>
      <td>Backend services at a large company</td>
      <td>Java, Kotlin, or C#</td>
    </tr>
    <tr>
      <td>iOS/macOS apps</td>
      <td>Swift</td>
    </tr>
    <tr>
      <td>Data plumbing, scripting, gluing tools together</td>
      <td>Bash, SQL</td>
    </tr>
    <tr>
      <td>Embedded or resource-constrained targets</td>
      <td>Zig or C++</td>
    </tr>
  </tbody>
</table>

<p>If you genuinely don’t know, Go or TypeScript are the safest defaults — both have large hiring markets, both are approachable in under two weeks, and both show up constantly in the backend/frontend split that most teams use.</p>

<h2 id="common-footguns-when-learning-a-new-language-this-way">Common footguns when learning a new language this way</h2>

<ul>
  <li><strong>Course-hopping instead of finishing</strong> — starting three 10-day courses in the same week means finishing none of them; the daily tasks build on each other, and losing the thread halfway through is worse than not starting.</li>
  <li><strong>Copy-pasting the code-along instead of typing it</strong> — you’ll pass the day’s task without understanding it, then stall on the capstone, which deliberately doesn’t hand you the pattern.</li>
  <li><strong>Skipping the capstone</strong> — it’s the only part of each course that tests whether you can combine ideas from multiple days, which is the actual skill you’re building, not any single day’s syntax.</li>
  <li><strong>Treating 10 days as “fluent”</strong> — these courses get you to “can read the language, can build something small and working, knows where to look next.” That’s the honest bar; real fluency takes shipping real code over months, not a sprint.</li>
  <li><strong>Ignoring the language’s own tooling</strong> — every course leans on the language’s standard formatter/linter/test runner (<code class="language-plaintext highlighter-rouge">gofmt</code>, <code class="language-plaintext highlighter-rouge">cargo test</code>, <code class="language-plaintext highlighter-rouge">rustfmt</code>, etc.) rather than fighting it; skipping that step means fighting style debates with yourself for months afterward.</li>
</ul>

<h2 id="you-know-a-course-is-actually-done-when">You know a course is actually done when…</h2>

<ul class="task-list">
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You completed the capstone without re-reading the day-by-day code-alongs from scratch</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can write the “hello world” equivalent from memory, with correct syntax, no lookup</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You know the language’s standard way to run tests and format code, and have actually run both at least once</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You can explain the one or two concepts that felt hardest on day one, in your own words, without the course’s explanation in front of you</li>
  <li class="task-list-item"><input type="checkbox" class="task-list-item-checkbox" disabled="disabled" />You’ve pushed the capstone to a Git repo — see <a href="/2026/07/10/getting-started-with-git/">Getting Started with Git</a> if that step itself is the unfamiliar part</li>
</ul>

<p>Each course has daily code-alongs, a concrete task, and a capstone. Start at the <a href="/courses/languages/">hub</a> and pick one language this week.</p>]]></content><author><name></name></author><category term="languages" /><category term="tutorials" /><category term="getting-started" /><summary type="html"><![CDATA[Multi-day hands-on courses for the languages teams actually hire for, each with a daily task and a capstone — plus SQL, Zig, and Bash.]]></summary></entry><entry><title type="html">LLM Mastery: 50 articles from tokens to your own tiny model</title><link href="https://shardalearningcenter.github.io/2026/07/10/llm-mastery-50-articles/" rel="alternate" type="text/html" title="LLM Mastery: 50 articles from tokens to your own tiny model" /><published>2026-07-10T00:00:00+00:00</published><updated>2026-07-10T00:00:00+00:00</updated><id>https://shardalearningcenter.github.io/2026/07/10/llm-mastery-50-articles</id><content type="html" xml:base="https://shardalearningcenter.github.io/2026/07/10/llm-mastery-50-articles/"><![CDATA[<p>We published <strong><a href="/courses/llm-mastery/">LLM Mastery</a></strong>: fifty in-depth articles that go from “what is a language model?” to training a tiny GPT you understand end to end, in the spirit of Andrej Karpathy — build tiny things, distrust vibes, love the loss curve.</p>

<h2 id="what-mastery-actually-means-here">What “mastery” actually means here</h2>

<p>Every article follows the same shape: one core idea, one piece of runnable code that demonstrates it, and one exercise that breaks if you don’t actually understand the idea. There are no slide-deck articles that just describe a concept in prose — if you can’t paste in code and watch a number change (a loss, an accuracy, a generated string), the article isn’t done. That’s a deliberate constraint on us, not just advice to you.</p>

<h2 id="who-its-for">Who it’s for</h2>

<ul>
  <li>Beginners who want first principles, not marketing decks about “AI”</li>
  <li>Engineers who’ve called ChatGPT-style APIs and want to know what’s actually happening underneath</li>
  <li>Anyone willing to type code, watch loss curves, and debug shape mismatches at 11pm</li>
</ul>

<p>You’ll get the most out of it if you’re comfortable with the material in our <a href="/2026/07/10/getting-started-with-python/">Getting Started with Python</a> and <a href="/2026/07/10/getting-started-with-pytorch/">Getting Started with PyTorch</a> posts — you don’t need to be an expert in either, but you should be able to run a script from the terminal and read a <code class="language-plaintext highlighter-rouge">RuntimeError</code> without panicking.</p>

<h2 id="shape-of-the-path">Shape of the path</h2>

<table>
  <thead>
    <tr>
      <th style="text-align: right">Phase</th>
      <th>Articles</th>
      <th>Focus</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right">Foundations</td>
      <td>01–10</td>
      <td>Tokens, loss, bigrams, the MLP mindset</td>
    </tr>
    <tr>
      <td style="text-align: right">Neural guts</td>
      <td>11–20</td>
      <td>Autograd from scratch, optimization, attention’s precursors</td>
    </tr>
    <tr>
      <td style="text-align: right">Transformers</td>
      <td>21–35</td>
      <td>Self-attention, GPT internals, systems concerns, scaling laws</td>
    </tr>
    <tr>
      <td style="text-align: right">Post-training</td>
      <td>36–50</td>
      <td>Fine-tuning (SFT), LoRA, RLHF/DPO, RAG, agents, safety, and a train-your-own capstone</td>
    </tr>
  </tbody>
</table>

<p>Start here: <a href="/courses/llm-mastery/01-what-is-a-language-model/">01. What is a Language Model, Really?</a></p>

<h2 id="a-concrete-sample-so-this-isnt-just-a-list-of-links">A concrete sample, so this isn’t just a list of links</h2>

<p>Article 4, <a href="/courses/llm-mastery/04-bigram-language-model/">Bigram Language Model</a>, has you build the simplest possible language model — a table of “given this character, what’s the next one” counts — and generate text from it. It’s a handful of lines, it runs in under a second, and the output is obviously bad (mostly gibberish with occasional real words), which is the point: it gives you a concrete, terrible baseline to improve on for the next 46 articles. By article 27, <a href="/courses/llm-mastery/27-implement-tiny-gpt/">Implement Tiny GPT</a>, you’re writing the actual self-attention and feed-forward blocks yourself, not importing them from a library.</p>

<h2 id="how-much-time-it-takes">How much time it takes</h2>

<p>Plan for 30–90 minutes per article if you type the code yourself rather than copy-pasting — and you should type it yourself, since transcription is where you notice what you don’t actually understand. Foundations and neural-guts articles (01–20) run comfortably on a CPU. Transformer-scale training (21–35 onward) benefits from a GPU, but every article’s <em>code</em> is written to run on a small config first so you can verify correctness before scaling up.</p>

<h2 id="faq">FAQ</h2>

<p><strong>Do I need a GPU?</strong> Not to follow along and understand the code. You’ll want one (even a modest one, or a free-tier cloud GPU) once you start training the larger configs in the transformers and post-training phases.</p>

<p><strong>Do I need to finish all 50 in order?</strong> Foundations (01–10) and Neural guts (11–20) build directly on each other — don’t skip those. Once you’re through Transformers (21–35), the post-training articles are more modular; RAG (art. 40) and agents (art. 41) don’t require you to have done fine-tuning (art. 36) first.</p>

<p><strong>What’s the capstone?</strong> Article 50, <a href="/courses/llm-mastery/50-capstone-train-your-own/">Capstone: Train Your Own</a>, has you train a small GPT on a dataset of your choosing, end to end, using everything from articles 01–49 — your own tokenizer decisions, your own architecture choices, your own eval.</p>

<h2 id="warm-up-posts-if-you-need-tooling-first">Warm-up posts, if you need tooling first</h2>

<p>These getting-started posts are short, practical, and each ends with something you build and verify yourself:</p>

<ul>
  <li><a href="/2026/07/10/getting-started-with-python/">Python</a></li>
  <li><a href="/2026/07/10/getting-started-with-git/">Git</a></li>
  <li><a href="/2026/07/10/getting-started-with-linux-shell/">Linux shell</a></li>
  <li><a href="/2026/07/10/getting-started-with-docker/">Docker</a></li>
  <li><a href="/2026/07/10/getting-started-with-javascript/">JavaScript</a></li>
  <li><a href="/2026/07/10/getting-started-with-sql/">SQL</a></li>
  <li><a href="/2026/07/10/getting-started-with-pytorch/">PyTorch</a></li>
  <li><a href="/2026/07/10/getting-started-with-fastapi/">FastAPI</a></li>
</ul>]]></content><author><name></name></author><category term="llm" /><category term="course" /><category term="deep-learning" /><summary type="html"><![CDATA[A Karpathy-style LLM course is live — 50 articles, each with runnable code, from 'what is a token' through training a tiny GPT you understand end to end.]]></summary></entry></feed>