<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged learning</title>
<link>https://coderlegion.com/tag/learning</link>
<description>Powered by Question2Answer</description>
<item>
<title>My AI Coding Project Files: What You Can Borrow</title>
<link>https://coderlegion.com/32111/my-ai-coding-project-files-what-you-can-borrow</link>
<description>&lt;p&gt;&lt;em&gt;A practical companion to my learning-in-public article about building EthoPipe.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I made these files because I kept getting lost between ideas, generated code, unfinished tasks, and new conversations. They were not a system I designed perfectly at the beginning. They grew as I worked out what would make the next session easier.&lt;/p&gt;
&lt;p&gt;This guide shows what each part does and what you might borrow. You do not need all of it, and you do not need my project’s limits. Start with one problem you want to make easier.&lt;/p&gt;
&lt;p&gt;You can explore &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/sothiss/ethopipe&quot;&gt;the EthoPipe repository&lt;/a&gt;. The file names below come from the working collection behind the article; older copies and local agent instructions may differ from what is currently public.&lt;/p&gt;
&lt;h3 id=&quot;1-pick-the-problem-not-the-whole-setup&quot;&gt;1. Pick the problem—not the whole setup&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The agent keeps adding too much:&lt;/strong&gt; start with &lt;code&gt;solo-maintainer.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ideas become code before you decide what you want:&lt;/strong&gt; try &lt;code&gt;idea-to-code.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You forget where you stopped:&lt;/strong&gt; borrow the structure in &lt;code&gt;project-state.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Every new conversation needs the same explanation:&lt;/strong&gt; look at &lt;code&gt;agent-handoff.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You want to understand a completed task:&lt;/strong&gt; use &lt;code&gt;walkthrough-and-tasks.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;You want a longer record of decisions:&lt;/strong&gt; start a journal like &lt;code&gt;JOURNEY.md&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For me, rules carry the project’s boundaries, procedures describe a recurring task, and scripts handle repetitive work. A plain note can be useful before you automate anything.&lt;/p&gt;
&lt;h3 id=&quot;2-project-rules-what-should-the-agent-leave-alone-&quot;&gt;2. Project rules: what should the agent leave alone?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;My files:&lt;/strong&gt; &lt;code&gt;solo-maintainer.md&lt;/code&gt;, &lt;code&gt;solo-maintainer-audit.md&lt;/code&gt;, and &lt;code&gt;AGENTS.MD&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My solo-maintainer rules keep EthoPipe within what I can follow and maintain. They include a Python-focused application, a direct-dependency budget, and limits on unnecessary infrastructure. Those are choices for my project, not requirements for yours.&lt;/p&gt;
&lt;p&gt;Here is a &lt;strong&gt;starter template to adapt&lt;/strong&gt;, not a verbatim copy of my rule:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Project: [What I am building]
Stack: [Languages and tools I have chosen]

Before adding a dependency:
Explain why the existing tools are not enough.

Before changing the architecture:
Describe the change and ask whether it belongs to this task.

Leave unrelated files and unfinished ideas alone.
If instructions conflict, ask instead of guessing.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I can change these boundaries when I choose another direction. They guide the agent’s actions without turning my current setup into a limit on what I can imagine.&lt;/p&gt;
&lt;h3 id=&quot;3-a-task-procedure-what-am-i-asking-for-now-&quot;&gt;3. A task procedure: what am I asking for now?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;My files:&lt;/strong&gt; &lt;code&gt;idea-to-code.md&lt;/code&gt;, &lt;code&gt;walkthrough-and-tasks.md&lt;/code&gt;, and &lt;code&gt;manage_ideas.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My idea-to-code procedure slows me down between brainstorming and implementation. I want to know why the task matters, what can change, and what result I am looking for. I also ask for a short walkthrough afterward so I can follow what happened.&lt;/p&gt;
&lt;p&gt;This adapted template is enough to start:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Objective: [One change I want]
Allowed files: [Existing files involved, including tests]
Leave alone: [Dependencies, configuration, unrelated features]

Before editing:
Read the relevant files and explain the plan.

Check:
[What should happen, and how to try or test it]

If the task needs to grow:
Tell me before making unrelated changes.

When finished:
Summarize changes, checks, blockers, and the next action.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I use numbered idea cards and an idea board when I have more to track. You can begin with a checklist in one file. A checked box is a reminder of work, not a replacement for looking at the result.&lt;/p&gt;
&lt;h3 id=&quot;4-a-checkpoint-where-did-i-stop-&quot;&gt;4. A checkpoint: where did I stop?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;My files:&lt;/strong&gt; &lt;code&gt;project-state.md&lt;/code&gt;, &lt;code&gt;PROJECT_STATE.md&lt;/code&gt;, and &lt;code&gt;manage_project_state.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This is the part I would most happily recommend to someone who keeps reopening a project and wondering what happened last time. Before finishing, leave a note precise enough for your future self.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Current focus: [The task I am working on]

Completed:
[What changed this session]

Still in progress:
[The file, function, question, or failing check]

Next action:
[One concrete thing to do when I return]

Watch out for:
[Uncommitted changes, blockers, or assumptions]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My script writes an active state file and dated copies, but the format is useful without a script. “Keep working on the API” is less useful to me than “open this test and check this failing case.”&lt;/p&gt;
&lt;h3 id=&quot;5-a-handoff-what-does-a-new-session-need-&quot;&gt;5. A handoff: what does a new session need?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;My files:&lt;/strong&gt; &lt;code&gt;agent-handoff.md&lt;/code&gt;, &lt;code&gt;generate_agent_handoff.py&lt;/code&gt;, and &lt;code&gt;compile_snapshot.py&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A handoff is a checkpoint with enough context for another conversation. My generator collects Git information and can run tests and refresh a snapshot when requested. The snapshot groups selected files; it is not automatically every file in the repository.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Project context: [What I am building and its current boundaries]
Immediate objective: [The next task]
Relevant files: [Only the context needed for this task]
Recent changes: [What the previous session changed]
Checks: [Observed results, or explicitly not run]
Blockers: [What remains unclear]
Next action: [Where to begin]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fresh conversations help me avoid mixing several tasks. The handoff helps me keep the continuity I actually need.&lt;/p&gt;
&lt;h3 id=&quot;6-a-journal-why-did-i-make-that-choice-&quot;&gt;6. A journal: why did I make that choice?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;My files:&lt;/strong&gt; &lt;code&gt;JOURNEY.md&lt;/code&gt;, &lt;code&gt;manage_journey.py&lt;/code&gt;, and &lt;code&gt;CHANGELOG.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The checkpoint says where to resume. The journal gives me a longer view: what confused me, what I tried, what changed, and what I learned. A changelog has a different job—summarizing project changes for people using or following it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Date and task:
Problem I was trying to solve:
What I tried:
What I changed or decided:
What helped, and what did not:
What I want to try next:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I do not need a grand milestone for every entry. A small discovery, an unsuccessful experiment, or a decision not to add something can be worth remembering.&lt;/p&gt;
&lt;h3 id=&quot;7-the-supporting-files-useful-when-you-need-them&quot;&gt;7. The supporting files: useful when you need them&lt;/h3&gt;
&lt;p&gt;You can add automation later. These are the other parts of my collection, grouped by what they help me do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Quality checks:&lt;/strong&gt; &lt;code&gt;code-quality-and-standards.md&lt;/code&gt;, &lt;code&gt;audit_maintainer_guardrails.py&lt;/code&gt;, &lt;code&gt;verify_open_source_standards.py&lt;/code&gt;, &lt;code&gt;OPEN_SOURCE_STANDARDS.md&lt;/code&gt;, and &lt;code&gt;OPEN_SOURCE_COMPLIANCE.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Release steps:&lt;/strong&gt; &lt;code&gt;release-governance.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bounded companion tasks:&lt;/strong&gt; &lt;code&gt;code-companion.md&lt;/code&gt; and &lt;code&gt;jules.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment and session context:&lt;/strong&gt; &lt;code&gt;sync-node.md&lt;/code&gt; and &lt;code&gt;AGENTS.MD&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Finding the documentation:&lt;/strong&gt; &lt;code&gt;SUMMARY.md&lt;/code&gt; and the checkpoint-archive &lt;code&gt;README.md&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The scripts are tied to my repository and environment. Read them before running them, and adapt paths, commands, and assumptions to your own project. You do not have to introduce a script just to use its underlying idea.&lt;/p&gt;
&lt;h3 id=&quot;8-the-older-files-show-how-the-setup-grew&quot;&gt;8. The older files show how the setup grew&lt;/h3&gt;
&lt;p&gt;My July material includes &lt;code&gt;implementation_plan.md&lt;/code&gt;, &lt;code&gt;walkthrough.md&lt;/code&gt;, &lt;code&gt;walkthrough1.md&lt;/code&gt;, &lt;code&gt;task-project.md&lt;/code&gt;, &lt;code&gt;update.md&lt;/code&gt;, &lt;code&gt;github_actions_audit.md&lt;/code&gt;, &lt;code&gt;github_actions_audit2.md&lt;/code&gt;, &lt;code&gt;technical_audit_report_1.md&lt;/code&gt;, &lt;code&gt;technical_audit_report3.md&lt;/code&gt;, &lt;code&gt;project_technical_report.md&lt;/code&gt;, and &lt;code&gt;agent-handoff-report.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The October records include &lt;code&gt;IDEAS_BOARD.md&lt;/code&gt;, &lt;code&gt;PROJECT_STATE.md&lt;/code&gt;, &lt;code&gt;STATE_20261012_152433.md&lt;/code&gt;, &lt;code&gt;SOLO_MAINTAINER_AUDIT_20261012_151845.md&lt;/code&gt;, &lt;code&gt;SOLO_MAINTAINER_AUDIT_20261012_160520.md&lt;/code&gt;, &lt;code&gt;OPEN_SOURCE_COMPLIANCE.md&lt;/code&gt;, and &lt;code&gt;LLM_SNAPSHOT.MD&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the bigger narrative, I kept &lt;code&gt;JOURNEY.md&lt;/code&gt;, &lt;code&gt;CHANGELOG.md&lt;/code&gt;, &lt;code&gt;gemini-code-phase.md&lt;/code&gt;, &lt;code&gt;gemini-code-telemetry-sync.md&lt;/code&gt;, &lt;code&gt;project-timeline.md&lt;/code&gt;, and &lt;code&gt;antigravity-system-directive.md&lt;/code&gt;. The earlier &lt;code&gt;Adobe Firefly.docx&lt;/code&gt; article and comment screenshot are the creative starting point, rather than files to install.&lt;/p&gt;
&lt;p&gt;I would browse these for examples rather than copy their historical status into a current project. The July files show individual plans and findings; the October collection shows more repeatable record-keeping. The paperwork grew because I was learning what I needed—not because every project needs that many documents.&lt;/p&gt;
&lt;h3 id=&quot;9-adapt-the-format-to-your-tool&quot;&gt;9. Adapt the format to your tool&lt;/h3&gt;
&lt;p&gt;In Antigravity, &lt;a rel=&quot;nofollow&quot; href=&quot;https://antigravity.google/docs/rules&quot;&gt;rules&lt;/a&gt; are persistent project instructions, while &lt;a rel=&quot;nofollow&quot; href=&quot;https://antigravity.google/docs/skills&quot;&gt;skills&lt;/a&gt; package reusable task procedures. The current documentation explains the supported locations and activation behavior. My older workflow files can supply the content, but their old packaging may need changing.&lt;/p&gt;
&lt;p&gt;If you use another tool, look for its project-instruction and reusable-task features. Ask for help adapting the content, then check the documentation and try a small task. A file is useful only if the tool actually finds it.&lt;/p&gt;
&lt;h3 id=&quot;10-leave-room-for-experiments&quot;&gt;10. Leave room for experiments&lt;/h3&gt;
&lt;p&gt;You might ask for a few approaches without authorizing edits, try one in a separate experiment, and write down what you learned. That can be worthwhile even when nothing gets merged or published.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;These files are meant to make exploration easier—not turn learning into another set of hurdles.&lt;/strong&gt; Keep what helps. Change what does not. Your project can be a place to play.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI assistance:&lt;/strong&gt; I used Gemini and Microsoft Copilot to discuss, organize, and edit this guide. The project files and experience are mine.&lt;/p&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/32111/my-ai-coding-project-files-what-you-can-borrow</guid>
<pubDate>Thu, 22 Oct 2026 18:01:20 +0000</pubDate>
</item>
<item>
<title>I’m Still Learning AI Coding: The Rules and Workflows Helping Me Find My Way</title>
<link>https://coderlegion.com/31885/im-still-learning-ai-coding-the-rules-and-workflows-helping-me-find-my-way</link>
<description>&lt;p&gt;&lt;em&gt;What building EthoPipe is teaching me about AI tools, project rules, and leaving room to experiment&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Update: I've linked the companion guide :) &lt;/p&gt;
&lt;p&gt;After my post &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/31633/how-adobe-firefly-and-copilot-taught-me-to-prompt-for-code-part-1&quot;&gt;How Adobe Firefly and Copilot Taught Me to Prompt for Code (Part 1)&lt;/a&gt;, a reader asked whether smaller coding prompts worked better than one detailed prompt. I answered yes, but the question stayed with me. What did “smaller tasks” actually look like in my project—and where did rules and workflows fit?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I am not an expert in AI-assisted development. I am learning by building something I care about.&lt;/strong&gt; These are the files and habits helping me find my way. You might borrow one part, change another, and ignore the rest.&lt;/p&gt;
&lt;p&gt;I used to be strongly against AI. I refused to use it. Now I am writing about how I work with it—not because I suddenly have everything figured out, but because I changed my mind and started experimenting.&lt;/p&gt;
&lt;p&gt;My project is EthoPipe, an open-science Python project for canine ethology. At one point, I restarted because I was getting lost among ideas, generated code, instructions, and unfinished tasks. I wanted the tools to help me follow the project, not produce more work I could no longer understand.&lt;/p&gt;
&lt;p&gt;My Firefly experiments gave me a useful starting point. A short image prompt could work because the reference photo and settings carried part of the instruction. With coding, I began asking the same practical question: what does the agent need to know before I ask it to change something?&lt;/p&gt;
&lt;h3 id=&quot;1-decide-what-i-want-before-asking-the-agent-to-build-it&quot;&gt;1. Decide what I want before asking the agent to build it&lt;/h3&gt;
&lt;p&gt;I usually brainstorm in the Gemini app, then move to a coding agent when the direction is clearer. Sometimes I use Copilot for another perspective or when I feel stuck. An IDE agent can discuss ideas too; separating the conversations simply helps me avoid turning every possibility into a feature.&lt;/p&gt;
&lt;p&gt;An idea can stay an idea. Before moving to code, I ask: &lt;strong&gt;What am I changing? Which files belong to the task? What should stay untouched? How will I know it worked?&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;what-changed-between-july-and-october&quot;&gt;What changed between July and October&lt;/h4&gt;
&lt;p&gt;My July files already contain plans, audits, and walkthroughs. They helped me notice problems such as a filename mismatch and checks that were configured but not working. I was learning to pay attention to the work around the code, not just the code itself.&lt;/p&gt;
&lt;p&gt;By October, I had an idea board, more specific project rules, scripts for recording stopping points, and a handoff process. I did not design that system in advance. I kept adding or adapting a file when the same confusion came back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;These files make the next session easier.&lt;/strong&gt; Some guide the agent, some automate repetitive work, and some remind me where I left off.&lt;/p&gt;
&lt;h3 id=&quot;2-my-rules-guide-the-agent-not-my-imagination&quot;&gt;2. My rules guide the agent—not my imagination&lt;/h3&gt;
&lt;p&gt;When I moved my instructions from Gemini Code Assist to Antigravity, some did not work the way I expected. I had to redo parts of the setup. That was frustrating, but it also made me pay more attention to where the files belonged and how the tool used them.&lt;/p&gt;
&lt;p&gt;The distinction that helps me is simple: &lt;strong&gt;rules&lt;/strong&gt; describe the project’s boundaries; &lt;strong&gt;workflows&lt;/strong&gt; describe how I approach a recurring task; &lt;strong&gt;scripts&lt;/strong&gt; handle repetitive checks or record-keeping. Each has a different job.&lt;/p&gt;
&lt;p&gt;The packaging changes too. Antigravity now documents &lt;a rel=&quot;nofollow&quot; href=&quot;https://antigravity.google/docs/skills&quot;&gt;skills&lt;/a&gt; for reusable task procedures and &lt;a rel=&quot;nofollow&quot; href=&quot;https://antigravity.google/docs/rules&quot;&gt;rules&lt;/a&gt; for persistent instructions. I still call the overall process my workflow. If you borrow my files, check the current documentation for your tool rather than assuming the old folder structure will work unchanged.&lt;/p&gt;
&lt;p&gt;I still review what the agent changes. “Do not touch this” is a useful instruction, not a reason to stop paying attention.&lt;/p&gt;
&lt;h4 id=&quot;one-example-keeping-the-project-small-enough-for-me&quot;&gt;One example: keeping the project small enough for me&lt;/h4&gt;
&lt;p&gt;I do not want the agent adding a setup I cannot understand or maintain. My solo-maintainer rules limit dependencies, extra languages, and unnecessary infrastructure. Those boundaries fit EthoPipe, and I can revise them when the project needs something different.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A few lines from my &lt;code&gt;solo-maintainer.md&lt;/code&gt; file:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;- Direct runtime dependencies in pyproject.toml are capped at &amp;lt;= 8 packages.
- Always prefer Python's standard library
  (pathlib, tomllib, dataclasses, argparse, typing, sqlite3)
  over adding third-party packages.
- Keep EthoPipe as a single modular Python service
  (FastAPI + Supabase).
- Individual files must remain under 600 lines.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your version might need a frontend framework, another database, or more dependencies. The useful part is deciding what fits your project and writing it down, rather than explaining it again in every conversation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The constraints guide the AI’s actions, not my imagination.&lt;/strong&gt; I can explore another direction without authorizing new packages, rewritten files, or unrequested features. Not every possibility needs to become code.&lt;/p&gt;
&lt;h3 id=&quot;3-give-a-smaller-task-a-clear-purpose&quot;&gt;3. Give a smaller task a clear purpose&lt;/h3&gt;
&lt;p&gt;My idea-to-code file follows &lt;strong&gt;triage → specification → test first → minimal implementation → QA and journal.&lt;/strong&gt; That may sound formal, but it helps me pause long enough to decide what I need before files start changing.&lt;/p&gt;
&lt;p&gt;Instead of “improve my project,” I ask for one change and name the files involved—including tests. If the agent finds a larger issue, I want it to tell me rather than quietly turn the task into something else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here is an example of how I would describe a small task:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Objective:
Check the subject_id length boundary.

Allowed files:
src/pipeline/models.py
tests/test_models.py

Non-goals:
No dependency, CI, or unrelated refactoring changes.

Acceptance:
255 characters accepted; 256 rejected.

Process:
Inspect existing tests. Add a regression test only if missing.
Make the smallest necessary change. Run targeted and full checks.

Stop:
Ask if requirements conflict or the boundary is undocumented.

Closeout:
Changed files, commands and results, blockers, next action.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The prompt is not especially short; it is specific. In this example, the limit and tests already exist in my snapshot, so inspection comes before generating anything new.&lt;/p&gt;
&lt;p&gt;That is why my answer is not “always write short prompts.” A longer discussion can establish the direction; smaller tasks carry it forward. The rules provide project context, and the task says what I want now.&lt;/p&gt;
&lt;h3 id=&quot;4-leave-myself-a-way-to-come-back&quot;&gt;4. Leave myself a way to come back&lt;/h3&gt;
&lt;p&gt;I often open a new conversation for a particular task. It keeps unrelated ideas out of the way, but I do not want to explain the whole project again. My state and handoff files help bridge those sessions.&lt;/p&gt;
&lt;p&gt;The state file records what I was doing, where I stopped, and what comes next. A handoff adds relevant project changes and checks when requested. Neither me nor the agent has to remember every detail of the previous conversation.&lt;/p&gt;
&lt;h4 id=&quot;the-kind-of-note-that-helps-me-resume&quot;&gt;The kind of note that helps me resume&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;From my October 12 &lt;code&gt;PROJECT_STATE.md&lt;/code&gt; checkpoint:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Where We Stopped:
Completed project-state skill, workflow, CLI engine
(scripts/manage_project_state.py), and state.ps1 helper
with docs/states/ historical archiving.

Quick-Resume Action:
Proceed to localized storage adapter design for Kimball
Star Schema persistence or review project state via
.\scripts\state.ps1.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The note is ordinary—and that is why it helps. I can return to it and know where to start. One checkpoint recorded the state tooling in progress; the next gave me a new task. The files keep that thread intact.&lt;/p&gt;
&lt;h3 id=&quot;5-ask-for-help-read-the-docs-and-keep-adjusting&quot;&gt;5. Ask for help, read the docs, and keep adjusting&lt;/h3&gt;
&lt;p&gt;When I do not know how to write a rule or workflow, I ask Gemini to help. I explain what I want, work through a draft, compare it with the documentation, and try it. If I cannot describe the goal, that is a sign I need to explore the idea a little longer.&lt;/p&gt;
&lt;p&gt;I work with the agent, review changes, and ask for a walkthrough when we finish. I want to understand what happened and what comes next. The goal is not to pretend I could have written every line unaided; it is to learn enough to keep working on my project.&lt;/p&gt;
&lt;p&gt;Google’s learning resources—including GEAR and courses available through my student benefits and learning credits—helped me understand the tools. The documentation also helped explain why instructions from one environment needed adapting in another.&lt;/p&gt;
&lt;h3 id=&quot;6-coding-experience-helps-but-the-tool-still-needs-learning&quot;&gt;6. Coding experience helps—but the tool still needs learning&lt;/h3&gt;
&lt;p&gt;I come from outside the usual developer path. I cannot speak for someone with twenty years of coding experience, but I can offer a beginner’s perspective: I am still asking questions about how these tools work.&lt;/p&gt;
&lt;p&gt;Coding experience and familiarity with a coding agent are related, but not identical. Recognizing a bad abstraction or debugging a problem is valuable. Learning how a particular agent reads instructions, carries context, asks for approval, and handles a task is another skill. Experience gives you something to build on; it does not remove the need to learn the tool.&lt;/p&gt;
&lt;p&gt;I wonder whether the familiar setting makes that easy to overlook. It is still an editor, a terminal, and a repository, but some of the interaction now happens through conversation. Reading a tutorial or asking how a feature works does not diminish anyone’s experience.&lt;/p&gt;
&lt;p&gt;I would start with an ordinary task: explain an unfamiliar function, suggest a test, or make a change I can follow. Then I can inspect the result and adjust my instructions. That gives me room to learn without expecting the first prompt to solve everything.&lt;/p&gt;
&lt;p&gt;Having once refused to use AI, I am reluctant to insist that anyone must embrace it. But curiosity is enough reason to try. A question or mistake does not have to become a verdict on you—or on the technology.&lt;/p&gt;
&lt;h4 id=&quot;i-do-not-think-you-need-the-most-expensive-tool-to-begin&quot;&gt;I do not think you need the most expensive tool to begin&lt;/h4&gt;
&lt;p&gt;Some articles leave me with the impression that useful AI-assisted coding requires the newest model or most expensive subscription. My experience makes me question that. I think an accessible tool can produce decent work when it fits the project and you take time to learn it.&lt;/p&gt;
&lt;p&gt;I started with the access I had. Gemini became familiar, and search fits the way I research. An upgrade might help with a specific limitation, but I do not want a price tag to make someone feel they cannot begin. Learning to describe the task has mattered more to me than chasing the model everyone is discussing that week.&lt;/p&gt;
&lt;h3 id=&quot;7-experimentation-is-a-reason-to-build-not-a-consolation-prize&quot;&gt;7. Experimentation is a reason to build—not a consolation prize&lt;/h3&gt;
&lt;h4 id=&quot;a-personal-project-can-be-a-place-to-play&quot;&gt;A personal project can be a place to play&lt;/h4&gt;
&lt;p&gt;I do not know whether EthoPipe will become something bigger. It already gives me a reason to learn, make mistakes, ask questions, and try ideas. That is not a consolation prize if it never becomes a finished product. It is a reason to build.&lt;/p&gt;
&lt;p&gt;A personal project does not have to become a company, a polished product, or proof that you are a “real” developer. You can vibe code, run tests, discard an experiment, and start again. &lt;strong&gt;AI can help you be more creative, not just more productive.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I can ask for three approaches without authorizing file changes, choose one, and try it separately. I can ask the agent to explain the result—or discard it if it does not help. Exploring possibilities is different from adding all of them to the main project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The agent needs a clear job; my ideas need room.&lt;/strong&gt; I can change the rules, explore another direction, or leave an experiment unfinished. The boundaries help me follow the work, not postpone it.&lt;/p&gt;
&lt;h4 id=&quot;if-you-want-to-try-this-start-with-one-file&quot;&gt;If you want to try this, start with one file&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Pick a task you keep repeating or a problem that keeps confusing you.&lt;/li&gt;
&lt;li&gt;Write a small rule or procedure in your own words.&lt;/li&gt;
&lt;li&gt;Ask your AI tool to help clarify it, then check how your environment loads it.&lt;/li&gt;
&lt;li&gt;Try it on one manageable task and look at the result.&lt;/li&gt;
&lt;li&gt;Keep what helps, change what does not, and leave yourself a next step.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You do not need my full collection. I am sharing it because concrete examples would have helped me understand what people meant by rules and workflows. Take what solves a problem for you, then adapt the stack, limits, and language.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Smaller tasks help me, but I am not trying to make my ideas smaller.&lt;/strong&gt; I am giving the agent a clearer job while I keep learning and experimenting. The prompt says what to do now. What I imagine next is still up to me.&lt;/p&gt;
&lt;p&gt;What have you tried with AI simply to learn or explore? I would love to hear about the experiments—not just the finished products.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI assistance:&lt;/strong&gt; I used Gemini and Microsoft Copilot to brainstorm, organize, and edit this article. The experience and project files are mine.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My files:&lt;/strong&gt; the companion guide, “&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/32111/my-ai-coding-project-files-what-you-can-borrow&quot;&gt;My AI Coding Project Files: What You Can Borrow&lt;/a&gt;,” groups the rules, workflows, scripts, and records behind these examples so you can choose one useful starting point.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31885/im-still-learning-ai-coding-the-rules-and-workflows-helping-me-find-my-way</guid>
<pubDate>Tue, 20 Oct 2026 19:22:32 +0000</pubDate>
</item>
<item>
<title>How Adobe Firefly and Copilot Taught Me to Prompt for Code (Part 1)</title>
<link>https://coderlegion.com/31633/how-adobe-firefly-and-copilot-taught-me-to-prompt-for-code-part-1</link>
<description>&lt;p&gt;Every time the topic of AI comes up in developer spaces, the conversation usually splits into two extremes: people who claim AI takes &quot;zero effort&quot; because you just type a sentence and get a finished product, and people who dismiss prompting as a soft, non-technical skill that has nothing to do with &quot;real&quot; engineering.&lt;/p&gt;
&lt;p&gt;Anyone who has actually spent hours trying to force a foundation model to produce a specific, non-generic output knows that both takes are wrong.&lt;/p&gt;
&lt;p&gt;My goal wasn't to generate random fantasy landscapes or stock avatars. I wanted to recreate my dog—an American Pit Bull Terrier (APBT)—across distinct visual styles so I could design a clean, accurate brand logo. To do that, I set two strict rules for myself:&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;1.&lt;/strong&gt; &lt;strong&gt;Only Native Firefly&lt;/strong&gt;: I refused to jump to third-party models when things got difficult; I committed exclusively to the native Adobe Firefly model (working through Firefly Image 3 and 4, long before conversational in-canvas editing existed).&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;2. Only My Own Photos&lt;/strong&gt;: I never used stock presets or random internet pictures. Every single generation was fed exclusively with my own photographs of my dog.&lt;/p&gt;
&lt;p&gt;That self-imposed constraint turned into a masterclass in fighting statistical bias, understanding parameter pipelines, and sanitizing noisy inputs. Looking back, the exact workflow I had to build to get Firefly to respect canine anatomy is the same mental model required to get an LLM to write production-ready code.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(Note: This is the first part of a multi-part series exploring how creative AI workflows translate directly into software engineering and systems architecture.)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=15746578785841454019&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;my dog for reference
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.behance.net/gallery/226055205/Morgan-Vega?platform=direct&quot;&gt;Behance Project: Morgan &quot;Vega&quot;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;fighting-the-default-weight-why-lazy-prompts-fail-&quot;&gt;Fighting the &quot;Default Weight&quot; (Why Lazy Prompts Fail)&lt;/h3&gt;
&lt;p&gt;When I started experimenting with Firefly in 2024 and locked into a heavy daily workflow in 2025, I quickly hit a wall. If you type &quot;pitbull&quot; into an image model by itself, you do not get an accurate American Pit Bull Terrier.&lt;/p&gt;
&lt;p&gt;Because public stock datasets suffer from massive tag pollution—where people tag Mastiff crosses, Cane Corsos, American Bullies, and random block-headed mixes under the same umbrella term—the model’s latent space defaults to two extremes: either an exaggerated, short-legged caricature or a generic Labrador-mastiff hybrid with mushy features.&lt;/p&gt;
&lt;p&gt;Because I refused to switch to another generator, I had to learn how to override Firefly's statistical baseline using my own reference photos, active human feedback (RLHF), and structured parameter control.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Engineering Parallel:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;LLMs do the exact same thing with code. If you give a coding model a lazy prompt like &quot;build an API endpoint to process user data,&quot; its weights will pull straight toward the internet's most common, bloated, and outdated tutorials. To override the statistical default, you cannot rely on umbrella terms. You have to provide a structural blueprint and specify exact boundaries.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;how-firefly-actually-works-parameter-pipelines-amp-edge-copilot&quot;&gt;How Firefly Actually Works: Parameter Pipelines &amp;amp; Edge Copilot&lt;/h3&gt;
&lt;p&gt;Most people who haven't used Adobe Firefly assume it works like a simple chat box. In reality, Firefly’s interface is a structured parameter pipeline that separates geometry, style, and text tokens into distinct control layers.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=7167573050077163068&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=12213989863353670922&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Instead of relying on a single text prompt to do everything, Firefly breaks generation down into specific modules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;General &amp;amp; Content Settings&lt;/strong&gt;: Choosing the base model (&lt;em&gt;Firefly Image 3 / 4&lt;/em&gt;), locking the &lt;em&gt;Aspect ratio&lt;/em&gt;, switching &lt;em&gt;Content type&lt;/em&gt; between &lt;em&gt;Photo&lt;/em&gt; and &lt;em&gt;Art&lt;/em&gt;, and dialing the &lt;em&gt;Visual intensity&lt;/em&gt; slider.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Composition Reference (Structure)&lt;/strong&gt;: This is where I uploaded my own photos of my dog. Under the hood, Firefly uses this slot for edge detection and depth mapping, forcing the new generation to follow the physical geometry of the uploaded photo.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Style Reference &amp;amp; Effects Taxonomy:&lt;/strong&gt; Rather than guessing random adjectives, Firefly categorizes visual weights into structured buckets—&lt;em&gt;Movements&lt;/em&gt;, &lt;em&gt;Themes&lt;/em&gt;, &lt;em&gt;Techniques&lt;/em&gt;, &lt;em&gt;Effects&lt;/em&gt;, &lt;em&gt;Materials&lt;/em&gt;, and &lt;em&gt;Concepts&lt;/em&gt; (such as &lt;em&gt;Minimalism&lt;/em&gt;, &lt;em&gt;Flat design&lt;/em&gt;, &lt;em&gt;Anime&lt;/em&gt;, or Doodle drawing), alongside explicit dropdowns for &lt;em&gt;Color and tone&lt;/em&gt;, &lt;em&gt;Lighting&lt;/em&gt;, and &lt;em&gt;Camera angle&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To master this setup, I realized I needed to understand what all those art movements and techniques actually meant, and how Firefly's text parser expected them to be combined with my reference uploads.&lt;/p&gt;
&lt;p&gt;I opened &lt;strong&gt;Copilot in the Edge browser&lt;/strong&gt; and used it as my domain tutor and syntax compiler:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learning the Art Taxonomy:&lt;/strong&gt; I used Copilot to learn the technical differences between the styles available in Firefly—what separates a digital painting from continuous-contour linework, cross-hatched doodle styles, manga ink, or flat vector color blocking.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compiling Firefly-Specific Prompts&lt;/strong&gt;: Firefly didn't want chatty paragraphs; it wanted clean, comma-separated tokens that complemented the sidebar parameters. When the model kept cropping or distorting lower limbs, I used Copilot to help refine explicit anatomical and positional tokens (like &lt;em&gt;standing and showing his legs&lt;/em&gt;) that worked with my uploaded Composition Reference.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=14283121917887204311&quot; alt=&quot;&quot;&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.adobe.com/community/gallery/assets/75b0745c-b6e3-44a7-a2f4-26aa011cc19a&quot;&gt;Adobe Gallery&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=17280446436872332353&quot; alt=&quot;&quot;&gt;&lt;br&gt;
(&lt;code&gt;an American Pit Bull Terrier, black, standing, in a simple background, digital painting&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;One of my early experiments feeding my dog's photo into this pipeline was hand-picked by Adobe to be featured in the official &lt;strong&gt;Adobe Firefly Gallery&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you look at the prompt on that featured gallery card—&lt;code&gt;an American Pit Bull Terrier, black, standing, in a simple background, digital painting&lt;/code&gt;—a casual viewer might assume it took zero effort. What they don't see is how the pipeline worked together: because I fed my own photograph into the Composition Reference slot and gave continuous feedback on anatomically accurate runs, my photo carried the morphological constraints (the exact rose/half-pricked ears, the lean chest, the straight front assembly). That allowed the text prompt to stay surgical and declarative.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Engineering Parallel:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Prompting complex systems is configuration and technical specification. When your underlying architecture is solid—when your Pydantic schemas, type definitions, and structural interfaces are already passed into the system's parameters—you don't need to write a 500-word conversational essay begging the AI not to hallucinate. Your prompt can be a clean, concise instruction because the structural reference is already doing the heavy lifting.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-pre-editing-experiments-diagnosing-cross-attention-bleed&quot;&gt;The Pre-Editing Experiments: Diagnosing Cross-Attention Bleed&lt;/h3&gt;
&lt;p&gt;Even with Firefly's Composition Reference locking my dog's anatomy in place, pushing my raw photos across radically different art styles before in-canvas editing existed exposed a major flaw in how diffusion models read reference images.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=16697791684709526287&quot; alt=&quot;&quot;&gt; &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Manga drawing style of an American Pit Bull Terrier, black. 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=5702403718989328462&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=17990835431187139384&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; American Pit Bull Terrier, solid black, doodle style
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=6760457221779585084&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; American pit bull terrier, solid black, standing and showing his legs, doodle style 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=2369913449814901055&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;American pit bull terrier, solid black, standing and showing his legs, cartoon style
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=262291669817531290&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;black american pit bull terrier standing 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looking across these early runs—all generated from my own photos—you can see two things happening at once:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What Worked (Morphological Locking)&lt;/strong&gt;: Whether I pushed the engine toward stark white-on-black ink, clean &lt;em&gt;doodle style&lt;/em&gt; linework, &lt;em&gt;manga drawing style&lt;/em&gt;, or &lt;em&gt;cartoon style&lt;/em&gt; pop art, my dog's core anatomy, ear carriage, and muscle tone stayed true to my photos.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Where the Noise Leaked In (Environmental Contamination)&lt;/strong&gt;: Look closely at the backgrounds. Because Firefly's Composition Reference evaluates the entire uploaded image, it couldn't tell where my dog ended and the backyard began. In the manga generation, a metal pole and patio clutter from my original photo bled straight into the illustration. In the walking side-profiles, the model spent half its attention rendering the wooden deck planks and walls. And in the sketch and dark stencil, background shadows confused the style weights into hallucinating fake artist signatures and floating geometric shapes in the negative space.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;input-sanitization-the-1-1-cutout-breakthrough&quot;&gt;Input Sanitization: The 1:1 Cutout Breakthrough&lt;/h3&gt;
&lt;p&gt;Diagnosing why those background artifacts were happening led to my biggest workflow breakthrough: I started completely removing the background from my dog's photos and standardizing the canvas to a 1:1 square before uploading them into Firefly's Composition Reference&lt;/p&gt;
&lt;p&gt;Suddenly, every generation locked in without environmental bleed. Why?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Eliminating Cross-Attention Contamination:&lt;/strong&gt; In a raw outdoor photo, 60% of the pixels are grass, fences, poles, and shadows. The model’s attention layers try to apply your &lt;em&gt;Effects&lt;/em&gt; and &lt;em&gt;Styles&lt;/em&gt; across all that background noise at once. Stripping the background zeroed out those competing tokens, forcing 100% of the feature extraction onto the dog's silhouette.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Unambiguous Edge Maps:&lt;/strong&gt; Isolating the subject gave Firefly's &lt;em&gt;Composition Reference&lt;/em&gt; algorithm a stark, vector-clean perimeter to color and shade within—instantly eliminating the hallucinated signatures and floating background scribbles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Native Latent Geometry:&lt;/strong&gt; Setting the input cutout and Firefly's &lt;em&gt;Aspect ratio&lt;/em&gt; parameter to a centered 1:1 square aligned with the native geometry of diffusion grids, eliminating compositional drift and giving me the exact, isolated character assets I needed to composite my final brand logo by hand.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;The Engineering Parallel:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Garbage context in, garbage code out. Those metal poles and fake signatures in my early dog drawings are the exact visual equivalent of what happens when a developer dumps an entire cluttered repository, unformatted stack traces, and five unrelated files into an LLM context window and says &quot;my code is a mess, fix it.&quot;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-takeaway-and-what-s-next-in-this-series-&quot;&gt;The Takeaway (And What's Next in This Series)&lt;/h3&gt;
&lt;p&gt;For a long time, I fell into the trap of thinking that being &quot;good at prompting&quot; didn't count as a real technical skill. We are conditioned to believe that unless we are suffering through manual boilerplate or memorizing every flag from scratch, we are somehow taking a shortcut.&lt;/p&gt;
&lt;p&gt;But refusing to take the easy route—sticking exclusively to the Firefly model, feeding it only my own photography, using Edge Copilot to master art taxonomy and syntax, diagnosing attention bleed across dozens of test runs, and engineering a clean 1:1 preprocessing pipeline—taught me what prompting actually is. Whether you are steering a diffusion model away from tag-polluted training data or directing a coding agent through a production refactor, the core discipline is the same: systems thinking.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Coming up next in this series:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the next post, I’ll be sharing my full Behance project, where I break down the complete visual archive of my Firefly attempts—showing the direct before-and-after impact of the 1:1 cutout pipeline—and how I took those isolated AI assets into Photoshop to composite and finish the final brand logo. Stay tuned!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31633/how-adobe-firefly-and-copilot-taught-me-to-prompt-for-code-part-1</guid>
<pubDate>Sun, 18 Oct 2026 17:06:01 +0000</pubDate>
</item>
<item>
<title>List Of Machine Learning (ML) Models</title>
<link>https://coderlegion.com/31328/list-of-machine-learning-ml-models</link>
<description>&lt;p&gt;Found this list while training it into my new personal LLM, its a list of machine learning (ML) models, with descriptions and settings. They are fairly easy to use if you are comfortable on the command line, but it's technical enough that it won't sound familiar to most people, so its good reading. If you want the full info, copy below this line into your favorite AI and say &quot;Take this list of ML models, research them and give me a description and use case for each.&quot; or tell it what you need to do and ask which one of these will do that. Learning just the 4 types and understanding that you chain multiple ones together with / without an LLM in the process. Warning too, frontier models will not write good training data for these just like all training data, will either flag you or just write generic bad data on purpose, so never ask it to make you training data unless its open source (deepseek, qwen etc).&lt;/p&gt;
&lt;p&gt;Ok so main purpose first, 4 categories:&lt;/p&gt;
&lt;p&gt;CLASSIFIERS - Take input, predict a category label (a string).&lt;br&gt;
Example: input = &quot;I want to cancel my account&quot;, output = &quot;cancellation&quot;&lt;/p&gt;
&lt;p&gt;Requires labeled training data: each training row has input + a string label.&lt;/p&gt;
&lt;p&gt;Specific Models: KNN (K-Nearest Neighbors), NAIVE BAYES (Gaussian Naive Bayes), DNN (Deep Neural Network / MultilayerPerceptron), RANDOM FOREST, TREE (Classification Tree), ADABOOST (Adaptive Boosting), LOGISTIC REGRESSION&lt;/p&gt;
&lt;p&gt;REGRESSORS - Take input, predict a number (a float).&lt;br&gt;
Example: input = [sqft, bedrooms, age], output = 350000.00&lt;/p&gt;
&lt;p&gt;Requires labeled training data: each training row has input + a numeric label.&lt;/p&gt;
&lt;p&gt;Specific Models: RIDGE (Ridge Regression), KNN REGRESSOR, SVR (Support Vector Regression), MLP REGRESSOR (Neural Network Regressor), GRADIENT BOOST&lt;/p&gt;
&lt;p&gt;CLUSTERERS - Take input, assign it to a group (integer cluster ID).&lt;br&gt;
Example: input = [age, income, spend], output = 2 (meaning cluster 2)&lt;/p&gt;
&lt;p&gt;No labels needed. The algorithm discovers groups on its own.&lt;/p&gt;
&lt;p&gt;Specific Models: KMEANS, DBSCAN, FUZZY C-MEANS&lt;/p&gt;
&lt;p&gt;ANOMALY DETECTORS - Take input, flag it as normal (0) or anomaly (1).&lt;br&gt;
Example: input = [transaction_amount, time, location], output = 1 (anomaly)&lt;/p&gt;
&lt;p&gt;Train on normal/clean data only. Anything that looks different gets flagged.&lt;/p&gt;
&lt;p&gt;Specific Models: ISOLATION FOREST, LOCAL OUTLIER FACTOR, ROBUST Z-SCORE&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31328/list-of-machine-learning-ml-models</guid>
<pubDate>Fri, 16 Oct 2026 12:38:00 +0000</pubDate>
</item>
<item>
<title>How Online Learning Can Help Coaching Institutes Go Digital</title>
<link>https://coderlegion.com/31329/how-online-learning-can-help-coaching-institutes-go-digital</link>
<description>&lt;p&gt;Today’s students learn very differently than students did even just a few years ago. Although many still prefer sitting in a classroom and learning directly from a teacher, others want the flexibility to study from home, revisit recorded lessons or attend classes without traveling long distances.&lt;/p&gt;
&lt;p&gt;Coaching institutes see this change as a challenge and an opportunity. Students have never had so much choice and institutes need to find ways to meet their expectations, without compromising on the quality of teaching.&lt;/p&gt;
&lt;p&gt;The transition to digital can help. It enables the coaching centers to reach out to students beyond the immediate neighborhood, offer courses in different formats and make learning resources available when students need them. However, going online is not only about having classes on video call. Institutes also need an easy way to organize courses, share study materials, assess students and keep everything running smoothly.&lt;/p&gt;
&lt;p&gt;The right learning platform can make taking your institute online a practical next step rather than a daunting task.&lt;/p&gt;
&lt;p&gt;Why Digital Transformation of Coaching Institutes?&lt;/p&gt;
&lt;p&gt;Anyone who has run a coaching institute knows there are certain limitations of classroom teaching. The size of an institute is determined by the space, teachers and schedule. It is limited by the number of students it can accommodate. Traveling long distances each day can be hard for students who live far away, and students who miss a class might find it hard to catch up.&lt;/p&gt;
&lt;p&gt;Digital learning addresses some of these challenges.&lt;/p&gt;
&lt;p&gt;Students can attend online classes, study materials and recorded lectures whenever they want to revise a topic. This is especially useful for competitive exam preparation, where students often need to practice again and again, revisit concepts they find difficult.&lt;/p&gt;
&lt;p&gt;The biggest benefit for institutes is the chance to reach more learners without having to set up another physical center. For example, a coaching institute in one city can begin online courses for students in other parts of India.&lt;/p&gt;
&lt;p&gt;It also allows for trying out different formats of teaching. Institutes can continue with regular batches in the classroom and also start recorded courses, online revision sessions or extra practice classes.&lt;/p&gt;
&lt;p&gt;The idea isn’t to replace classroom teaching. To provide students with more ways to learn and help institutes make better use of their teaching resources.&lt;/p&gt;
&lt;p&gt;What Is a Good Online Learning Platform for Institutes?&lt;/p&gt;
&lt;p&gt;Online classes are pretty easy to begin with. The complications can come in when the number of students increases, and you’re managing the entire learning experience.&lt;/p&gt;
&lt;p&gt;Study materials can be scattered across different apps, assignments can get lost in messages, and teachers can spend too much time on administrative tasks. Students, however, may have a hard time locating the correct lesson or recalling what they need to complete.&lt;/p&gt;
&lt;p&gt;A good online learning platform can collect these activities together.&lt;/p&gt;
&lt;p&gt;It must be user-friendly. Students should be able to access their courses and learning materials without needing to call for technical assistance all the time. Teachers should be able to create lessons and conduct their classes without having to deal with a system that is overly complex.&lt;/p&gt;
&lt;p&gt;Another important aspect is course management. Institutes need an easy way to organize lessons, upload resources and make learning content available to students. Assessments, quizzes and assignments can help to further support the learning process by helping students to practice and giving teachers a better understanding of their progress.&lt;/p&gt;
&lt;p&gt;It is also worth considering what the institute will need in the future. A platform that works for a small batch should optimally be able to handle growth as more students enrolll and new courses are added.&lt;/p&gt;
&lt;p&gt;When selecting the &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://imbibe.in/itlearn/&quot;&gt;best online education platform for institutes to go digital&lt;/a&gt;&lt;/strong&gt;, institutes should consider what actually matters to their teachers and students. The best solution is not always the one with the most features. It is the one that helps you to teach, makes learning more accessible and simplifies everyday administration.&lt;/p&gt;
&lt;p&gt;How to Create Your Own Branded Online Academy&lt;/p&gt;
&lt;p&gt;Imagine students who are aware of your institute’s name, have faith in your faculty, and are eager to learn from you even if they can’t attend your physical classes. An online academy provides you a way to make that happen.&lt;/p&gt;
&lt;p&gt;Institutes can create their own digital learning environment rather than using third-party learning platforms. The institute forms its identity outside of the classroom and students can take courses associated with the institute.&lt;/p&gt;
&lt;p&gt;For instance, a coaching center for competitive exams could provide recorded foundation courses to students from all over India. An institute of language can start online classes for working professionals who cannot attend regular classes. A training center could offer short courses to learners who want to acquire specific skills.&lt;/p&gt;
&lt;p&gt;For institutes, a platform to start their own branded online academy in India can be the first step to this model. Depending on the features it offers it can be a tool for organizing courses, sharing learning materials, managing student access, and delivering educational content online.&lt;/p&gt;
&lt;p&gt;Of course, starting an academy is more than just setting up the technology. Institutes need to think about what courses they will offer, who their clients will be and how they will offer a valuable learning experience. Course content should be clear, engaging and appropriate to the needs of the target audience.&lt;/p&gt;
&lt;p&gt;It is equally important to consider what happens after a student enrolls. Easy access to lessons, clear instructions, timely communication and appropriate support for learners can make a real difference.&lt;/p&gt;
&lt;p&gt;A successful online academy is a natural extension of what an institute already does well. Technology takes care of the teaching there, but the quality of the teaching is what gives the students a reason to stay.&lt;/p&gt;
&lt;p&gt;Hybrid Learning: A Viable Option for Coaching Institutes?&lt;/p&gt;
&lt;p&gt;Not every institute is prepared to go full online, and many students still enjoy the experience of learning in a classroom. Hybrid learning is the middle ground.&lt;/p&gt;
&lt;p&gt;In simple terms, hybrid learning is a mixture of classroom teaching and online classes or digital learning resources. Students can attend regular classes at the institute and revise at home with recorded lectures or study materials online. Institutes may also offer selected sessions online to students who live farther away.&lt;/p&gt;
&lt;p&gt;Take an example of a coaching center where some students come to class physically and others can’t travel regularly. By employing a hybrid model the institute can maintain service to both groups without completely reworking its existing instructional model.&lt;/p&gt;
&lt;p&gt;Students enjoy greater flexibility, while teachers can tap into digital resources to enrich classroom instruction. Recorded lessons, when available, can be especially useful for students who need to review a difficult concept before an examination.&lt;/p&gt;
&lt;p&gt;A platform for hybrid classes in coaching institutes can help to bring these learning activities in a more organized environment. Depending on the platform’s capabilities, institutes may be able to manage live sessions, recorded lessons, study materials and assessments along with their regular teaching activities.&lt;/p&gt;
&lt;p&gt;The key is to ensure that online students receive the same amount of attention and clarity as those in the classroom. Timetables should be simple to follow, learning materials accessible and communication between teachers and students consistent.&lt;/p&gt;
&lt;p&gt;Hybrid learning, if planned well, helps institutes retain the intimacy of classroom teaching, while giving students the convenience of digital education.&lt;/p&gt;
&lt;p&gt;How an Institute Can Grow Through Digital Learning&lt;/p&gt;
&lt;p&gt;Growth does not have to mean opening another branch or increasing the number of classroom batches. Sometimes it means finding new ways to transfer the knowledge and teaching expertise that an institute already has.&lt;/p&gt;
&lt;p&gt;Digital learning can help make that happen.&lt;/p&gt;
&lt;p&gt;An institute may introduce additional courses, provide recorded lessons for revision, or design specialized programs for students with different learning needs. It can also reach learners in locations where it would be difficult or expensive to establish a physical center.&lt;/p&gt;
&lt;p&gt;There are practical benefits, as well. If learning materials and course activities are managed through a central platform, teachers can spend less time looking for resources or doing repetitive administrative work. Students find it easier to find the materials they need, and institutes can run their courses more consistently.&lt;/p&gt;
&lt;p&gt;In addition, digital learning can assist institutions in adapting to changing student expectations. Some learners like live classes, others prefer recorded lessons and many are helped by a combination of both. An institute can reach a wider audience by providing a variety of learning formats.&lt;/p&gt;
&lt;p&gt;But technology is only one part of the story. Institutes still need good teachers, relevant courses, good communication and a true understanding of their students. A digital platform can be of help, but it cannot substitute the trust that is built by quality teaching and a good learning experience.&lt;/p&gt;
&lt;p&gt;ItLearn: Making Digital Learning Accessible&lt;/p&gt;
&lt;p&gt;ItLearn by Imbibe Technologies is an education technology platform to assist institutions in delivering and managing digital learning. ItLearn offers an opportunity for coaching institutes who wants to go beyond traditional classroom teaching to explore a more structured approach to online education. It can support an institute’s efforts to make learning accessible to students beyond the physical institute and to adapt to evolving expectations around digital education. Institutions considering this transition can reach out to the ItLearn team to learn how the platform’s functionalities cater to their requirements for course delivery, learning materials, and student accessibility. No matter if the goal is to enhance an existing digital learning model or to discover new ways to deliver education, the first step is to determine what the institute needs and how ItLearn can fit into that plan.&lt;/p&gt;
&lt;p&gt;Conclusion: Bring Your Institute Outside the Classroom&lt;/p&gt;
&lt;p&gt;Going digital needn’t mean giving up on traditional teaching. That means more opportunities for your institution to reach students, to share knowledge, to fit learning more seamlessly into their lives.&lt;/p&gt;
&lt;p&gt;Maybe you want to reach students in other cities. Or maybe you want to offer your own online courses. Or maybe you want to combine classroom and digital learning. Whatever your goal, the right platform will make the changeover easier to handle.&lt;/p&gt;
&lt;p&gt;First look at the needs of your students and where your existing model of teaching could be more elastic. Then select a solution that supports those needs without unnecessarily complicating everyday teaching.&lt;/p&gt;
&lt;p&gt;Perhaps your institute's growth will begin outside the classroom next. Get in touch with ItLearn from Imbibe Technologies today to talk about your digital learning needs and find out how you can take your coaching institute online, reach out to more students and create a future-ready learning experience.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31329/how-online-learning-can-help-coaching-institutes-go-digital</guid>
<pubDate>Fri, 16 Oct 2026 10:04:19 +0000</pubDate>
</item>
<item>
<title>Personal Branding for Developers: Make Your Growth Visible</title>
<link>https://coderlegion.com/31036/personal-branding-for-developers-make-your-growth-visible</link>
<description>&lt;p&gt;Learning to code gives us something to build. Sharing what we learn gives people a way to find us.&lt;/p&gt;
&lt;p&gt;A project on LinkedIn. A question in a community. A hackathon experience turned into a blog post.&lt;/p&gt;
&lt;p&gt;Each offers a glimpse of what we care about, how we think, and where we are heading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For me, personal branding starts with making that growth visible.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;give-people-a-reason-to-remember-you&quot;&gt;Give people a reason to remember you&lt;/h3&gt;
&lt;p&gt;&quot;I'm learning to become a developer&quot; is an introduction. Our work adds the details.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A project&lt;/strong&gt; shows the problem we wanted to solve.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A blog post&lt;/strong&gt; explains our decisions and discoveries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A thoughtful contribution&lt;/strong&gt; shows how we engage with others.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Over time, those details give someone a reason to think:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;They care about this problem. I'd like to work with them.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We can start while we are still learning. Explaining what we tried, where we struggled, and what changed is already worth sharing.&lt;/p&gt;
&lt;h3 id=&quot;community-shows-me-where-to-grow&quot;&gt;Community shows me where to grow&lt;/h3&gt;
&lt;p&gt;I have participated in hackathons, and I'm active in other communities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I love communities because they help me see where I need to grow.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Learning alone can make it difficult to spot the gaps in my understanding. Other people bring questions, approaches, and experiences I might never encounter on my own.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A question I struggle to answer becomes something to explore.&lt;/li&gt;
&lt;li&gt;Someone else's approach reveals an option I hadn't considered.&lt;/li&gt;
&lt;li&gt;Feedback helps me decide what to work on next.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is part of what I want to document here: &lt;strong&gt;what I did, what I learned, and what I still need to figure out.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;connections-travel-further-than-we-expect&quot;&gt;Connections travel further than we expect&lt;/h3&gt;
&lt;p&gt;An online community can become a local meetup. A familiar username can become a teammate.&lt;/p&gt;
&lt;p&gt;Sometimes, we find ourselves sharing experiences and encouraging one another in places we never expected.&lt;/p&gt;
&lt;p&gt;Relationships grow through small actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remembering someone's project.&lt;/li&gt;
&lt;li&gt;Offering useful feedback.&lt;/li&gt;
&lt;li&gt;Sharing an opportunity.&lt;/li&gt;
&lt;li&gt;Celebrating a small step forward.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Participating means becoming someone who helps others move forward, too.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;why-i-m-starting-opportunity-map&quot;&gt;Why I'm starting Opportunity Map&lt;/h3&gt;
&lt;p&gt;Writing brings together three things I want to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reflect:&lt;/strong&gt; understand what I'm learning and where I'm going.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Share:&lt;/strong&gt; make my experience useful to someone else.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Connect:&lt;/strong&gt; meet people I could learn, build, or work with.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Through &lt;strong&gt;Opportunity Map&lt;/strong&gt;, I'll share:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lessons from projects and hackathons.&lt;/li&gt;
&lt;li&gt;Experiences and discoveries from communities.&lt;/li&gt;
&lt;li&gt;Opportunities to get involved, from internships to volunteering.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I want this series to connect possibilities with practical next steps.&lt;/p&gt;
&lt;h3 id=&quot;start-with-one-honest-contribution&quot;&gt;Start with one honest contribution&lt;/h3&gt;
&lt;p&gt;Share a problem you solved. Explain a decision you made. Write about a question that changed your thinking.&lt;/p&gt;
&lt;p&gt;A useful starting point is:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;What did I try, what did I learn, and what would I do differently?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think of what comes next as an ocean of possibilities. I don't know who will read a post or where a conversation might lead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;But sharing my work leaves a coordinate someone else can find.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is where my Opportunity Map begins.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/31036/personal-branding-for-developers-make-your-growth-visible</guid>
<pubDate>Wed, 14 Oct 2026 09:11:05 +0000</pubDate>
</item>
<item>
<title>Learning `just`: A Practical Task Runner</title>
<link>https://coderlegion.com/30937/learning-just-a-practical-task-runner</link>
<description>&lt;p&gt;I've been learning &lt;code&gt;just&lt;/code&gt; recently.&lt;/p&gt;
&lt;p&gt;I came across it while setting up a Python project and looking for a simple way to collect all the commands I use during development.&lt;/p&gt;
&lt;p&gt;This started with something I got used to in Elixir.&lt;/p&gt;
&lt;p&gt;One of the things I really like about Elixir projects is that you can put the common development tasks behind simple commands/aliases:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mix format
mix test
mix ci
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You don't have to remember every command that runs underneath. The project gives you a small set of commands and you use those.&lt;/p&gt;
&lt;p&gt;I wanted something similar for a Python project.&lt;/p&gt;
&lt;p&gt;For example, instead of remembering all the different checks I need to run, I wanted to be able to do this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;just ci
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and let the project take care of the rest.&lt;/p&gt;
&lt;h3 id=&quot;my-first-thought-was-make&quot;&gt;My first thought was Make&lt;/h3&gt;
&lt;p&gt;Make was the obvious choice.&lt;/p&gt;
&lt;p&gt;I've used Make a lot, and it works. It's installed almost everywhere, most developers have seen a Makefile before, and there is a huge amount of existing knowledge around it.&lt;/p&gt;
&lt;p&gt;So I started there.&lt;/p&gt;
&lt;p&gt;The setup was pretty small:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-make&quot;&gt;format:
	uv run ruff format --check .

lint:
	uv run ruff check .

typecheck:
	uv run mypy src/

test:
	uv run pytest --cov=src --cov-report=term-missing

ci: format lint typecheck test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That gave me exactly what I wanted.&lt;/p&gt;
&lt;p&gt;I could run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;make format
make lint
make typecheck
make test
make ci
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing complicated.&lt;/p&gt;
&lt;p&gt;But while doing this, I kept seeing &lt;code&gt;just&lt;/code&gt; mentioned in newer projects.&lt;/p&gt;
&lt;p&gt;That made me curious.&lt;/p&gt;
&lt;p&gt;Not because I thought Make was bad. I still use it a lot, and I have no problem reaching for it.&lt;/p&gt;
&lt;p&gt;I just wondered what &lt;code&gt;just&lt;/code&gt; was doing differently.&lt;/p&gt;
&lt;p&gt;Was it basically Make with different syntax? Was it easier to use? Did it solve some of the small annoyances that come with Make?&lt;/p&gt;
&lt;p&gt;So I decided to rebuild the same thing in &lt;code&gt;just&lt;/code&gt; and see.&lt;/p&gt;
&lt;h3 id=&quot;the-just-version&quot;&gt;The &lt;code&gt;just&lt;/code&gt; version&lt;/h3&gt;
&lt;p&gt;The nice thing was that I didn't have to change any of the actual commands.&lt;/p&gt;
&lt;p&gt;I just changed the wrapper around them:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-just&quot;&gt;format:
    uv run ruff format --check .

lint:
    uv run ruff check .

typecheck:
    uv run mypy src/

test:
    uv run pytest --cov=src --cov-report=term-missing

ci: format lint typecheck test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's pretty much the whole file.&lt;/p&gt;
&lt;p&gt;Now I can run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;just format
just lint
just typecheck
just test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or everything with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;just ci
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ci&lt;/code&gt; recipe depends on the other four recipes, so &lt;code&gt;just ci&lt;/code&gt; runs them in order and stops when one fails.&lt;/p&gt;
&lt;p&gt;For what I was trying to do, it felt very familiar.&lt;/p&gt;
&lt;p&gt;And that was probably the first thing I noticed. I didn't really have to learn a new way of thinking about the problem.&lt;/p&gt;
&lt;h3 id=&quot;a-few-things-i-liked-about-just&quot;&gt;A few things I liked about &lt;code&gt;just&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The basic syntax is very similar to Make.&lt;/p&gt;
&lt;p&gt;You have a recipe, optional dependencies, and then the commands to run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-just&quot;&gt;recipe_name: dependency1 dependency2
    command to run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But there are a few differences that make sense when you're using it mainly as a task runner.&lt;/p&gt;
&lt;h4 id=&quot;no-phony&quot;&gt;No &lt;code&gt;.PHONY&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;With Make, you often have something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-make&quot;&gt;.PHONY: format lint typecheck test ci
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's because Make is also a build system and cares about files and timestamps.&lt;/p&gt;
&lt;p&gt;For these development commands, I don't really need that behaviour.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;just&lt;/code&gt; doesn't use file timestamps to decide whether a recipe needs to run, so there is no &lt;code&gt;.PHONY&lt;/code&gt; declaration to maintain.&lt;/p&gt;
&lt;h4 id=&quot;no-tab-problem&quot;&gt;No tab problem&lt;/h4&gt;
&lt;p&gt;Then there is the classic Make issue where a command needs to be indented with a tab.&lt;/p&gt;
&lt;p&gt;I've definitely lost time to that one.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;just&lt;/code&gt;, you don't have that same tab requirement. You use normal indentation.&lt;/p&gt;
&lt;p&gt;It might seem like a small thing, but I like small improvements in tools I use.&lt;/p&gt;
&lt;h4 id=&quot;just---list&quot;&gt;&lt;code&gt;just --list&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;This was another nice surprise.&lt;/p&gt;
&lt;p&gt;You can run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;just --list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and get a list of the recipes available in the project.&lt;/p&gt;
&lt;p&gt;You can add comments above recipes too:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-just&quot;&gt;# Format check
format:
    uv run ruff format --check .

# Run tests
test:
    uv run pytest --cov=src --cov-report=term-missing
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Those comments can then show up when listing the recipes.&lt;/p&gt;
&lt;p&gt;You can also make &lt;code&gt;just&lt;/code&gt; itself show the list:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-just&quot;&gt;default:
    @just --list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So a developer can clone the project and run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;just
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to see what is available.&lt;/p&gt;
&lt;p&gt;I like that because the task runner becomes a small piece of documentation.&lt;/p&gt;
&lt;h3 id=&quot;but-is-just-actually-better-than-make-&quot;&gt;But is &lt;code&gt;just&lt;/code&gt; actually better than Make?&lt;/h3&gt;
&lt;p&gt;I'm not sure that's the right question.&lt;/p&gt;
&lt;p&gt;Make is still a great choice in plenty of situations.&lt;/p&gt;
&lt;p&gt;If I'm working on a project that already has a Makefile, I'm not going to suggest replacing it with &lt;code&gt;just&lt;/code&gt; just because &lt;code&gt;just&lt;/code&gt; is newer.&lt;/p&gt;
&lt;p&gt;And if I'm writing something that needs to work in an environment where Make is already installed, that is a pretty good reason to use Make.&lt;/p&gt;
&lt;p&gt;The thing I found interesting about &lt;code&gt;just&lt;/code&gt; is that it feels more focused on the thing I was actually trying to do.&lt;/p&gt;
&lt;p&gt;I wasn't building anything based on file timestamps.&lt;/p&gt;
&lt;p&gt;I just wanted names for common commands and a way to combine them.&lt;/p&gt;
&lt;p&gt;For that use case, &lt;code&gt;just&lt;/code&gt; is pretty nice.&lt;/p&gt;
&lt;p&gt;But it also comes with a trade-off. Unlike Make, you probably have to install it first.&lt;/p&gt;
&lt;p&gt;So the comparison for me looks something like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Make         &lt;/th&gt;&lt;th&gt;just&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Already installed &lt;/td&gt;&lt;td&gt;Often        &lt;/td&gt;&lt;td&gt;Usually not&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Task runner       &lt;/td&gt;&lt;td&gt;Yes          &lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Build system      &lt;/td&gt;&lt;td&gt;Yes          &lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;.PHONY&lt;/code&gt;          &lt;/td&gt;&lt;td&gt;Often needed &lt;/td&gt;&lt;td&gt;Not needed&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Tab requirement   &lt;/td&gt;&lt;td&gt;Yes          &lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Task listing      &lt;/td&gt;&lt;td&gt;DIY          &lt;/td&gt;&lt;td&gt;Built in&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Cross-platform    &lt;/td&gt;&lt;td&gt;Possible     &lt;/td&gt;&lt;td&gt;Designed for it&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Neither one wins across the board.&lt;/p&gt;
&lt;p&gt;Make has ubiquity.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;just&lt;/code&gt; has a nice developer experience.&lt;/p&gt;
&lt;p&gt;Which one makes more sense depends on the project.&lt;/p&gt;
&lt;h3 id=&quot;there-are-other-options-too&quot;&gt;There are other options too&lt;/h3&gt;
&lt;p&gt;Once I started looking into this, I found quite a few other tools doing roughly the same thing.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Task&lt;/code&gt; (go-task) was one I came across. It uses a YAML &lt;code&gt;Taskfile.yml&lt;/code&gt; and has some useful features around variables and templating.&lt;/p&gt;
&lt;p&gt;For Python, &lt;code&gt;poethepoet&lt;/code&gt; was another interesting option. It lets you put tasks in &lt;code&gt;pyproject.toml&lt;/code&gt;, which can be nice if you don't want another configuration file.&lt;/p&gt;
&lt;p&gt;And if you're working in Node, npm scripts already give you a similar setup through &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;They all solve a similar problem.&lt;/p&gt;
&lt;p&gt;You have a bunch of commands that developers need to run, and you give those commands names so people don't have to remember all the details.&lt;/p&gt;
&lt;p&gt;The differences are mostly around syntax, ecosystem, and how much functionality you want.&lt;/p&gt;
&lt;h3 id=&quot;so-am-i-switching-to-just-&quot;&gt;So, am I switching to &lt;code&gt;just&lt;/code&gt;?&lt;/h3&gt;
&lt;p&gt;For everything? No.&lt;/p&gt;
&lt;p&gt;I'm still going to use Make.&lt;/p&gt;
&lt;p&gt;If I open a project and there is a Makefile, I'll use it. If I'm working on infrastructure where Make is already expected, I'll probably use it there too.&lt;/p&gt;
&lt;p&gt;But for projects I start myself, I can see myself reaching for &lt;code&gt;just&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The thing I liked most was how little there was to learn.&lt;/p&gt;
&lt;p&gt;My existing commands stayed exactly the same. I just put a small, readable interface around them.&lt;/p&gt;
&lt;p&gt;That brought me back to why I started looking at this in the first place.&lt;/p&gt;
&lt;p&gt;In Elixir, I'm used to having commands like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mix ci
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;that give the project a simple interface for common development tasks.&lt;/p&gt;
&lt;p&gt;With Python, I found myself wanting the same thing.&lt;/p&gt;
&lt;p&gt;Make was one way to do it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;just&lt;/code&gt; is another.&lt;/p&gt;
&lt;p&gt;I'm glad I spent the time learning it. Not because it replaced Make, but because now I know another tool that fits this particular problem really well.&lt;/p&gt;
&lt;p&gt;And sometimes that's enough of a reason to try something new.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30937/learning-just-a-practical-task-runner</guid>
<pubDate>Tue, 13 Oct 2026 12:45:10 +0000</pubDate>
</item>
<item>
<title>A Beginner’s Guide to Git and GitHub: From Installation to Your First Push</title>
<link>https://coderlegion.com/30826/a-beginners-guide-to-git-and-github-from-installation-to-your-first-push</link>
<description>&lt;p&gt;Starting my journey in &lt;strong&gt;Data Science, Analysis, and AI at LUXDevHQ&lt;/strong&gt; felt like learning a new language while trying to build a house. One of the most important tools I’ve discovered along the way is &lt;strong&gt;Version Control&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this guide, I’ll walk you through:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setting up Git Bash  &lt;/li&gt;
&lt;li&gt;Connecting Git to GitHub  &lt;/li&gt;
&lt;li&gt;Mastering essential &lt;strong&gt;push&lt;/strong&gt; and &lt;strong&gt;pull&lt;/strong&gt; commands  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-what-is-git-and-why-does-it-matter-&quot;&gt;1. What Is Git and Why Does It Matter?&lt;/h3&gt;
&lt;p&gt;Git is a &lt;strong&gt;Version Control System (VCS)&lt;/strong&gt;. Think of it as a &lt;em&gt;save-point system&lt;/em&gt; for your code.&lt;/p&gt;
&lt;h4 id=&quot;why-is-git-important-&quot;&gt;Why is Git important?&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;⏪ Time Travel&lt;/strong&gt; – If you break your code, you can roll back to a version that worked.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🤝 Collaboration&lt;/strong&gt; – Multiple people can work on the same project without overwriting each other’s work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🧪 Experimentation&lt;/strong&gt; – You can create &lt;em&gt;branches&lt;/em&gt; to try new features without affecting the main project.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-setting-up-your-environment&quot;&gt;2. Setting Up Your Environment&lt;/h3&gt;
&lt;h4 id=&quot;step-a-install-git-bash&quot;&gt;Step A: Install Git Bash&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com&quot;&gt;Git&lt;/a&gt; and download Git for your OS (I used Windows).&lt;/li&gt;
&lt;li&gt;Run the installer.  &lt;blockquote&gt;&lt;p&gt;💡 &lt;em&gt;Pro tip:&lt;/em&gt; You can keep the default settings for most options.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;After installation, search for &lt;strong&gt;Git Bash&lt;/strong&gt; in your applications. It looks like a terminal window.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;step-b-configure-your-identity&quot;&gt;Step B: Configure Your Identity&lt;/h4&gt;
&lt;p&gt;To let GitHub know who is uploading code, configure your global Git settings:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git config --global user.name &quot;Your Name&quot;
git config --global user.email *Emails are not allowed*
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-secure-your-connection-setting-up-ssh-keys&quot;&gt;3. Secure Your Connection: Setting Up SSH Keys&lt;/h3&gt;
&lt;p&gt;Using SSH is the professional standard. It’s more secure and saves you from typing your password every time you push code.&lt;/p&gt;
&lt;h4 id=&quot;step-1-generate-your-ssh-key&quot;&gt;Step 1: Generate Your SSH Key&lt;/h4&gt;
&lt;p&gt;Open Git Bash and enter (replace with your GitHub email):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh-keygen -t ed25519 -C *Emails are not allowed*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;•File Location: Press Enter to use the default location.&lt;br&gt;
•Passphrase: As a beginner, I left this empty for convenience.&lt;/p&gt;
&lt;h4 id=&quot;step-2-add-key-to-the-ssh-agent&quot;&gt;Step 2: Add Key to the SSH Agent&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;eval &quot;$(ssh-agent -s)&quot;
ssh-add ~/.ssh/id_ed25519
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-3-add-the-public-key-to-github&quot;&gt;Step 3: Add the Public Key to GitHub&lt;/h4&gt;
&lt;p&gt;1.Copy the key: &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat ~/.ssh/id_ed25519.pub 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2.Go to GitHub: Settings → SSH and GPG keys → New SSH Key.&lt;/p&gt;
&lt;p&gt;3.Give it a name (e.g., &quot;My Learning Laptop&quot;) and paste your key into the &quot;Key&quot; box.&lt;/p&gt;
&lt;h4 id=&quot;step-4-test-the-connection&quot;&gt;Step 4: Test the Connection&lt;/h4&gt;
&lt;p&gt;Run: &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -T *Emails are not allowed*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Success Check! If you see &quot;Hi [YourUsername]! You've successfully authenticated&quot;, you are ready!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-navigating-and-creating-your-project&quot;&gt;4. Navigating and Creating Your Project&lt;/h3&gt;
&lt;p&gt;Learning to navigate via Git Bash makes you much faster than using a mouse! Use these commands to create your first repository:&lt;br&gt;
•Check Location: &lt;strong&gt;pwd&lt;/strong&gt; (Print Working Directory).&lt;br&gt;
•Go to Desktop: &lt;strong&gt;cd Desktop&lt;/strong&gt;&lt;br&gt;
•Create Folder: &lt;strong&gt;mkdir my-first-repo&lt;/strong&gt;&lt;br&gt;
•Enter Folder: &lt;strong&gt;cd my-first-repo&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-tracking-changes-the-core-workflow-&quot;&gt;5. Tracking Changes (The Core Workflow)&lt;/h3&gt;
&lt;p&gt;Before sending code to GitHub, Git needs to &quot;track&quot; it locally. Run these inside your project folder:&lt;br&gt;
1.Initialize Git: &lt;strong&gt;git init&lt;/strong&gt; (Starts tracking the folder).&lt;br&gt;
2.Check Status: &lt;strong&gt;git status&lt;/strong&gt; (See what files Git notices).&lt;br&gt;
3.Add Files: &lt;strong&gt;git add .&lt;/strong&gt; (Stages all changes to be saved).&lt;br&gt;
4.Commit: &lt;strong&gt;git commit -m &quot;My first commit&quot;&lt;/strong&gt; (Creates the &quot;save point&quot;).&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-pushing-code-to-github&quot;&gt;6. Pushing Code to GitHub&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Pushing&lt;/strong&gt; sends your local save points to the cloud.&lt;/p&gt;
&lt;h4 id=&quot;step-a-create-the-repository-on-github-com&quot;&gt;Step A: Create the Repository on GitHub.com&lt;/h4&gt;
&lt;p&gt;1.Log into GitHub, click the + icon → New repository.&lt;br&gt;
2.Name it (e.g., my-first-project) and keep it Public.&lt;br&gt;
3.Important: Leave &quot;Add a README&quot; unchecked to avoid conflicts.&lt;br&gt;
4.Click Create repository.&lt;/p&gt;
&lt;h4 id=&quot;step-b-connect-and-push&quot;&gt;Step B: Connect and Push&lt;/h4&gt;
&lt;p&gt;On the GitHub setup page, click SSH and copy the URL. Then run these commands one by one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git remote add origin *Emails are not allowed*:your-username/repo-name.git
git push -u origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;7-pulling-code-from-github&quot;&gt;7. Pulling Code from GitHub&lt;/h3&gt;
&lt;p&gt;If you work on a different computer, use Pull to download the latest updates from the cloud:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git pull origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-resources-to-keep-learning&quot;&gt;📚 Resources to Keep Learning&lt;/h3&gt;
&lt;p&gt;•&lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/doc&quot;&gt;Official Git Documentation&lt;/a&gt;&lt;br&gt;
•&lt;a rel=&quot;nofollow&quot; href=&quot;https://skills.github.com/&quot;&gt;GitHub Skills: Interactive Courses&lt;/a&gt;&lt;br&gt;
•&lt;a rel=&quot;nofollow&quot; href=&quot;https://learngitbranching.js.org/&quot;&gt;Visualizing Git Commands (Game)&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;conclusion-congratulations-you-ve-just-set-up-a-professional-dev-workflow-git-can-be-tricky-at-first-but-keep-practicing-and-it-will-become-second-nature-if-you-ran-into-any-issues-drop-a-comment-below-and-let-s-help-each-other-out-&quot;&gt;Conclusion: Congratulations! You've just set up a professional dev workflow. Git can be tricky at first, but keep practicing and it will become second nature. If you ran into any issues, drop a comment below and let's help each other out!&lt;/h3&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30826/a-beginners-guide-to-git-and-github-from-installation-to-your-first-push</guid>
<pubDate>Mon, 12 Oct 2026 16:20:10 +0000</pubDate>
</item>
<item>
<title>Building CodeHarborHub: My Journey Toward Open-Source Developer Education</title>
<link>https://coderlegion.com/30320/building-codeharborhub-my-journey-toward-open-source-developer-education</link>
<description>&lt;h3 id=&quot;introduction&quot;&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Learning to code is easier when you have access to clear explanations, practical examples, and projects that help you apply what you learn.&lt;/p&gt;
&lt;p&gt;But learning doesn't stop after completing a tutorial. Developers also need opportunities to practice, explore real-world problems, and contribute to meaningful projects.&lt;/p&gt;
&lt;p&gt;That idea is one of the reasons I'm building &lt;strong&gt;CodeHarborHub&lt;/strong&gt;, an open-source developer education platform.&lt;/p&gt;
&lt;p&gt;Website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://codeharborhub.github.io/&quot;&gt;https://codeharborhub.github.io/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;why-i-m-building-codeharborhub&quot;&gt;Why I'm Building CodeHarborHub&lt;/h3&gt;
&lt;p&gt;I want to help make modern technology easier to explore through practical learning resources and community-driven development.&lt;/p&gt;
&lt;p&gt;CodeHarborHub brings together the idea of learning, building, and contributing. The goal is to create a place where developers can explore tutorials, understand programming concepts, work on projects, and participate in open source.&lt;/p&gt;
&lt;p&gt;For me, the mission is not just to publish content. It's to encourage developers to move from reading about technology to actually building with it.&lt;/p&gt;
&lt;h3 id=&quot;what-i-m-working-on&quot;&gt;What I'm Working On&lt;/h3&gt;
&lt;p&gt;My work focuses on several areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Developer education:&lt;/strong&gt; Creating structured tutorials and learning resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Web development:&lt;/strong&gt; Building modern websites and developer-focused tools.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open source:&lt;/strong&gt; Encouraging contributions, collaboration, and shared learning.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI-powered applications:&lt;/strong&gt; Exploring practical ways to use AI in software products.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'm also building projects such as AI Buddies and Algo, which reflect my interests in AI applications and problem-solving practice.&lt;/p&gt;
&lt;p&gt;AI Buddies: &lt;a rel=&quot;nofollow&quot; href=&quot;https://ajay-dhangar.github.io/ai-buddies/&quot;&gt;https://ajay-dhangar.github.io/ai-buddies/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Algo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://ajay-dhangar.github.io/algo/&quot;&gt;https://ajay-dhangar.github.io/algo/&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;technology-and-development&quot;&gt;Technology and Development&lt;/h3&gt;
&lt;p&gt;My current technical interests include JavaScript, TypeScript, React, Node.js, Tailwind CSS, Docusaurus, Supabase, and AI/LLM technologies.&lt;/p&gt;
&lt;p&gt;I enjoy exploring how these tools can work together to build useful applications and accessible learning experiences.&lt;/p&gt;
&lt;p&gt;The exact technology choices depend on the needs of each project. I believe in choosing tools that solve the problem rather than adding complexity without a clear benefit.&lt;/p&gt;
&lt;h3 id=&quot;why-open-source-matters&quot;&gt;Why Open Source Matters&lt;/h3&gt;
&lt;p&gt;Open source creates opportunities to learn from other developers, review different approaches, improve documentation, and build software collaboratively.&lt;/p&gt;
&lt;p&gt;It also allows learners to gain practical experience beyond isolated tutorials.&lt;/p&gt;
&lt;p&gt;I want CodeHarborHub to support this spirit of collaboration by encouraging useful contributions and helping people learn from the process of building real projects.&lt;/p&gt;
&lt;h3 id=&quot;what-s-next-&quot;&gt;What's Next?&lt;/h3&gt;
&lt;p&gt;I'm continuing to improve the platform, develop learning resources, and explore ways to make developer education more practical and engaging.&lt;/p&gt;
&lt;p&gt;There is still plenty to learn and build. I see this as a long-term journey shaped by experimentation, feedback, and community participation.&lt;/p&gt;
&lt;h3 id=&quot;let-s-connect&quot;&gt;Let's Connect&lt;/h3&gt;
&lt;p&gt;I'm always interested in connecting with developers, learners, open-source contributors, and people building useful technology.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Website:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://codeharborhub.github.io/&quot;&gt;https://codeharborhub.github.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Ajay-Dhangar&quot;&gt;https://github.com/Ajay-Dhangar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Portfolio:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://ajay-dhangar.github.io/&quot;&gt;https://ajay-dhangar.github.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/ajay-dhangar/&quot;&gt;https://www.linkedin.com/in/ajay-dhangar/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you're building something in web development, AI, or open source, I'd love to hear about it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What would make learning to code more practical and accessible for you?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let's learn, build, and contribute together.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30320/building-codeharborhub-my-journey-toward-open-source-developer-education</guid>
<pubDate>Thu, 08 Oct 2026 18:42:18 +0000</pubDate>
</item>
<item>
<title>Use AI to Get Smarter, Not Lazier</title>
<link>https://coderlegion.com/30045/use-ai-to-get-smarter-not-lazier</link>
<description>&lt;p&gt;I promise this isn't another post saying AI is making us dumber.&lt;/p&gt;
&lt;p&gt;AI does make us faster, and we can't afford to give that up. But if you're not careful, &quot;faster&quot; quietly turns into &quot;I shipped this and can't explain it&quot;. This post is about a way to use AI that keeps the speed &lt;strong&gt;and&lt;/strong&gt; leaves you understanding what you did.&lt;/p&gt;
&lt;p&gt;I'll explain the problem first, then walk through one of my day-to-day tasks, triaging static analysis (SAST) findings, done two ways: with a one-line &quot;just triage it&quot; prompt, and with AI used to help me &lt;em&gt;understand&lt;/em&gt;. Both reach the same verdicts. Only one of them leaves me able to defend those verdicts.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;Prefer video?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/z_A_UVAFx0M&quot;&gt;https://youtu.be/z_A_UVAFx0M&lt;/a&gt; &lt;/p&gt;
&lt;h3 id=&quot;-the-long-way-is-where-the-learning-happens&quot;&gt;️ The long way is where the learning happens&lt;/h3&gt;
&lt;p&gt;Every task is a trip from &quot;task&quot; to &quot;done&quot;. The long way is reading, searching, trying, failing and trying again. It's slow, but that's where you pick up tools, debugging habits, patterns, mental models, trade-offs and skills.&lt;/p&gt;
&lt;p&gt;Humans take the path of least resistance, so we've always looked for shortcuts. Copying an answer from Stack Overflow was the classic one.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/6gjh35r8x80ls09doucs.png&quot; alt=&quot;The long way picks up tools, debugging, patterns, mental models, trade-offs and skills. Stack Overflow is the first shortcut&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;-ai-is-the-ultimate-shortcut&quot;&gt;⚡ AI is the ultimate shortcut&lt;/h3&gt;
&lt;p&gt;Now a huge share of our tasks can be done, fully or partly, by telling AI: &lt;em&gt;&quot;do it for me&quot;&lt;/em&gt;. Most of the time the result is good enough.&lt;/p&gt;
&lt;p&gt;That shortcut skips the long way entirely. We also can't simply refuse it: what's expected of us has gone up, because everyone can now get more done with AI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/0ihgzc1ruyyg7a86o6za.png&quot; alt=&quot;The AI shortcut goes straight from task to done and skips everything on the long way.&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;-cognitive-debt&quot;&gt;️ Cognitive debt&lt;/h3&gt;
&lt;p&gt;Skip the learning once and nothing happens. But each time you take the shortcut, you finish a task without fully understanding how it works or why it was done that way. That gap is &lt;strong&gt;cognitive debt&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/rieuveeosiynfmv7fvsv.gif&quot; alt=&quot;Every task done by AI pushes the cognitive debt meter up, towards the threshold&quot;&gt;&lt;/p&gt;
&lt;p&gt;The debt stays invisible for a while. Then one day you hit a problem AI can't solve, and you have no idea where you are, where to start or where to go. We've all heard of codebases that were thrown away because rebuilding them was easier than understanding them, sometimes even for the AI that wrote them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/of7mswf52z6njelwos1n.png&quot; alt=&quot;The debt boils over: a problem AI can't solve, and you don't know where to start.&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;-the-fix-a-shortcut-that-balances-doing-and-understanding&quot;&gt;⚖️ The fix: a shortcut that balances doing and understanding&lt;/h3&gt;
&lt;p&gt;The answer isn't to stop using AI. It's to take a slightly &lt;strong&gt;longer&lt;/strong&gt; shortcut, one that balances two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Doing:&lt;/strong&gt; finishing the task, fast.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Understanding:&lt;/strong&gt; knowing what was done, how, and why (the decisions and trade-offs), and learning to do it yourself.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You're still much faster than on the long way, but you arrive smarter instead of in debt.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/fmrgowby8nyhrj9lh5to.png&quot; alt=&quot;A balanced shortcut: understand, trace, evidence, experiment. Still fast, and the debt meter stays low.&quot;&gt;&lt;/p&gt;
&lt;p&gt;You don't need to do this for everything. Set a threshold based on risk: a throwaway script can take the fast path. A decision you'll have to defend, like a security finding, can't.&lt;/p&gt;
&lt;p&gt;The good news: AI is also very good at helping you &lt;strong&gt;understand&lt;/strong&gt;. Here's what that looks like on a real task.&lt;/p&gt;
&lt;h3 id=&quot;the-demo-triaging-semgrep-findings&quot;&gt;The demo: triaging Semgrep findings&lt;/h3&gt;
&lt;p&gt;The app is a small Express + PostgreSQL order tracker, built in layers like a real app: route → middleware → controller → service → repository. Customers list their orders, download CSV exports and leave comments. Admins see revenue reports.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;Demo repo:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mohamed-osama-aboelkheir/ai-sast-triage-demo&quot;&gt;mohamed-osama-aboelkheir/ai-sast-triage-demo&lt;/a&gt;. It's deliberately vulnerable. &lt;code&gt;main&lt;/code&gt; is the clean app; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mohamed-osama-aboelkheir/ai-sast-triage-demo/tree/reference-output&quot;&gt;&lt;code&gt;reference-output&lt;/code&gt;&lt;/a&gt; has everything the AI produced in this post.&lt;/p&gt;
&lt;p&gt;I ran Semgrep (the free &lt;code&gt;p/default&lt;/code&gt; ruleset) and got a handful of findings, all marked as blocking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL injection in &lt;code&gt;orderRepository.js&lt;/code&gt; (&lt;code&gt;ORDER BY ${orderBy}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;SQL injection in &lt;code&gt;reportRepository.js&lt;/code&gt; (&lt;code&gt;FROM ${tableName}&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Path traversal in &lt;code&gt;exportService.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;XSS through an unescaped EJS tag in &lt;code&gt;comments.ejs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;No CSRF middleware in &lt;code&gt;app.js&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As an AppSec engineer, my job is to decide which are real, how severe they are, and which ones are worth an engineer's time. And let's say I don't know this codebase well.&lt;/p&gt;
&lt;h3 id=&quot;-approach-1-just-triage-it-&quot;&gt;⚡ Approach 1: &quot;just triage it&quot;&lt;/h3&gt;
&lt;p&gt;The shortest path: open Claude Code, paste the Semgrep output, and ask:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;semgrep scan returned the below results, please triage the findings and let me know which are true and which are false positive, and suggest severity and fixes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A couple of minutes later I had a tidy answer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL injection in &lt;code&gt;ORDER BY&lt;/code&gt;: &lt;strong&gt;true positive, High&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Path traversal in &lt;code&gt;/exports&lt;/code&gt;: &lt;strong&gt;true positive, High&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Table name in &lt;code&gt;reportRepository&lt;/code&gt;: false positive&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;%- %&amp;gt;&lt;/code&gt; XSS: false positive&lt;/li&gt;
&lt;li&gt;Missing CSRF middleware: false positive&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's a good answer, and it even explains itself. But look at the two SQL injection findings:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// src/repositories/orderRepository.js — flagged
`SELECT id, total, status, created_at FROM orders
 WHERE user_id = $1 AND ($2::text IS NULL OR status = $2)
 ORDER BY ${orderBy}`
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// src/repositories/reportRepository.js — flagged
`SELECT count(*)::int AS orders, coalesce(sum(total), 0) AS revenue FROM ${tableName}`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same library, same pattern: a variable pasted into the SQL text. One is &quot;High&quot;, the other is &quot;false positive&quot;. If I don't know the codebase, I can't tell whether that's right. I could still copy the verdicts into tickets and move on. That's fast, and it's exactly how cognitive debt builds up. And the moment an engineer pushes back with &lt;em&gt;&quot;this isn't exploitable&quot;&lt;/em&gt;, I have nothing to say.&lt;/p&gt;
&lt;p&gt;So let's take the two SQL injection findings and do it again, with AI helping me understand.&lt;/p&gt;
&lt;h3 id=&quot;approach-2-understand-trace-decide-prove&quot;&gt;Approach 2: understand, trace, decide, prove&lt;/h3&gt;
&lt;p&gt;I packaged each step as a Claude Code skill in the &lt;code&gt;appsec-skills&lt;/code&gt; plugin, so you can repeat it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;/plugin marketplace add mohamed-osama-aboelkheir/appsec-untangled-resources
/plugin install appsec-skills@appsec-untangled
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each step leaves a file you can read, check and reuse, not chat text you throw away.&lt;/p&gt;
&lt;h4 id=&quot;1-understand-read-the-code-like-a-story&quot;&gt;1. ️ Understand: read the code like a story&lt;/h4&gt;
&lt;p&gt;Looking at the two flagged files on their own doesn't tell me much. What's &lt;code&gt;orderRepository.find&lt;/code&gt;? Who calls &lt;code&gt;reportRepository.summary&lt;/code&gt;? Code is written to be used by people, so the useful context is the &lt;strong&gt;story&lt;/strong&gt;: which user action reaches this line, and what passes through on the way.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Let's start by understanding the context using the &lt;code&gt;/appsec-skills:code-walkthrough&lt;/code&gt; skill to explain how the affected backend routes work, and how users interact with them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;code-walkthrough&lt;/code&gt; skill produces two things for each flow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;sequence diagram&lt;/strong&gt;: one column per layer, a colour band per phase,  for security controls,  for untrusted input and ⚠️ for the sink;&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;CodeTour&lt;/strong&gt; file, a VS Code extension that walks you through the code step by step, the way the engineer who wrote it would. Step N in the tour is step N in the diagram.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's the story for &lt;code&gt;GET /orders&lt;/code&gt; (the full diagram also covers the 401/400/500 exits):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/7x7buz5wooxu5kxcq0vo.png&quot; alt=&quot;Sequence diagram for GET /orders: routing, authentication, input handling, data access and response, with the ORDER BY sink at step 11.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Two steps stand out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Step 8:&lt;/strong&gt; the controller validates &lt;code&gt;status&lt;/code&gt; against an allowlist, but &lt;strong&gt;&lt;code&gt;sort&lt;/code&gt; is not checked&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Steps 9–10:&lt;/strong&gt; &lt;code&gt;sort&lt;/code&gt; is renamed &lt;code&gt;orderColumn&lt;/code&gt;, then &lt;code&gt;orderBy&lt;/code&gt;, on its way down to the query.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/iyp3n11gvp6awvi8dgcn.png&quot; alt=&quot;CodeTour in VS Code: step 8 highlights the status allowlist, and sort goes through unchecked.&quot;&gt;&lt;/p&gt;
&lt;p&gt;The second story, &lt;code&gt;GET /reports/:reportId&lt;/code&gt;, is different. It needs an admin, and the controller uses &lt;code&gt;reportId&lt;/code&gt; to &lt;strong&gt;look up&lt;/strong&gt; an entry in a config object. Unknown IDs get a 404:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/etg1120nxo1vplqva4yn.png&quot; alt=&quot;Sequence diagram for GET /reports/:reportId: auth, admin role check, config lookup, then FROM ${tableName}.&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you were paying attention, you may already see the difference. If not, that's what the next step is for.&lt;/p&gt;
&lt;h4 id=&quot;2-trace-source-to-sink&quot;&gt;2.  Trace: source-to-sink&lt;/h4&gt;
&lt;p&gt;Source-to-sink analysis asks one question: &lt;strong&gt;does user input reach the dangerous function, and does anything stop it on the way?&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Now using this context, perform the &lt;code&gt;/appsec-skills:source-to-sink&lt;/code&gt; analysis for both findings, and share a verdict on whether each is a true or false positive.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The skill walks back from each sink, hop by hop. It draws one chain of code cards per source, with the value in bold on every line and arrows named after what the value is called next.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finding 1:&lt;/strong&gt; a red chain (user input) goes from &lt;code&gt;req.query.sort&lt;/code&gt; straight to &lt;code&gt;ORDER BY&lt;/code&gt;. The only check on the way () covers &lt;code&gt;status&lt;/code&gt;, not &lt;code&gt;sort&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/99lyf8rawyzthtpw301p.png&quot; alt=&quot;Source-to-sink trace: req.query.sort → orderColumn → orderBy → ORDER BY ${orderBy}. The status allowlist doesn't cover sort.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here's that path in the code (&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mohamed-osama-aboelkheir/ai-sast-triage-demo/blob/main/src/controllers/ordersController.js#L5-L13&quot;&gt;controller&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mohamed-osama-aboelkheir/ai-sast-triage-demo/blob/main/src/services/orderService.js#L3-L4&quot;&gt;service&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// src/controllers/ordersController.js
exports.list = async (req, res) =&amp;gt; {
  const { status, sort } = req.query;
  if (status &amp;amp;&amp;amp; !ALLOWED_STATUSES.includes(status)) {   // only status is checked
    return res.status(400).json({ error: 'invalid status' });
  }
  const orders = await orderService.search({ status, orderColumn: sort }, req.user);
  res.json(orders);
};

// src/services/orderService.js
exports.search = (filters, user) =&amp;gt;
  orderRepository.find(user.id, filters.status, filters.orderColumn || 'created_at');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;$1&lt;/code&gt; and &lt;code&gt;$2&lt;/code&gt; are bound parameters, but column names can't be bound, so &lt;code&gt;ORDER BY ${orderBy}&lt;/code&gt; takes whatever the user sends. &lt;strong&gt;True positive.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finding 2:&lt;/strong&gt; the user's &lt;code&gt;reportId&lt;/code&gt; stops ✋ at a lookup into a hard-coded object. The value that actually reaches &lt;code&gt;FROM&lt;/code&gt; is a blue chain (config), not user input:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ajyx8xcaluwomv203177.png&quot; alt=&quot;Source-to-sink trace: reportId is only used as an object key; the table name comes from config/reports.js.&quot;&gt;&lt;/p&gt;
&lt;p&gt;(&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mohamed-osama-aboelkheir/ai-sast-triage-demo/blob/main/src/controllers/reportsController.js#L4-L9&quot;&gt;controller&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mohamed-osama-aboelkheir/ai-sast-triage-demo/blob/main/src/config/reports.js&quot;&gt;config&lt;/a&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// src/config/reports.js
module.exports = {
  daily:   { title: 'Orders in the last 24 hours', table: 'orders_daily_summary' },
  monthly: { title: 'Orders this month',           table: 'orders_monthly_summary' },
};

// src/controllers/reportsController.js
const definition = reports[req.params.reportId];   // input picks an entry...
if (!definition) return res.status(404).json({ error: 'unknown report' });
res.json(await reportService.build(definition));   // ...the table name comes from config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The user chooses &lt;em&gt;which&lt;/em&gt; trusted value is used. They never supply SQL. &lt;strong&gt;False positive.&lt;/strong&gt; It's still worth a low-priority fix (an allowlist, or &lt;code&gt;Object.hasOwn&lt;/code&gt;, so &lt;code&gt;/reports/constructor&lt;/code&gt; doesn't return a 500), but it's nowhere near as urgent as finding 1.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt; &lt;strong&gt;The two sinks look identical. Only the trace tells them apart.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I reached the same verdicts as the one-line prompt. The difference is that I can now &lt;strong&gt;defend&lt;/strong&gt; them: I can show an engineer the exact path, the missing check and the fix.&lt;/p&gt;
&lt;h4 id=&quot;3-decide-an-output-template-with-evidence&quot;&gt;3.  Decide: an output template with evidence&lt;/h4&gt;
&lt;p&gt;That worked for two findings. To do it at scale, I use another trick: an &lt;strong&gt;output template&lt;/strong&gt;. When I triage by hand, I look for the same things every time: the sink, the inputs that reach it, the path between them, the controls on that path, and then a verdict and a severity. So I make the AI fill in exactly those fields.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Now using this context, use &lt;code&gt;/appsec-skills:semgrep-triage&lt;/code&gt; to triage both findings.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;semgrep-triage&lt;/code&gt; skill runs &lt;code&gt;source-to-sink&lt;/code&gt; for every finding, checks each control against a wiki of mitigations and known bypasses, and writes one report. Here are the important parts for finding 1 (abridged):&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;F1 · SQL injection in &lt;code&gt;ORDER BY&lt;/code&gt; via &lt;code&gt;sort&lt;/code&gt; · ✅ True positive ·  High ⬆️&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Inputs reaching it:&lt;/strong&gt;  &lt;code&gt;req.query.sort&lt;/code&gt; (any logged-in user) &lt;strong&gt;reaches the sink unchanged&lt;/strong&gt;; ⚪ &lt;code&gt;'created_at'&lt;/code&gt; is the constant default.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controls on the path:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;requireAuth&lt;/code&gt;: decides &lt;em&gt;who&lt;/em&gt; can send &lt;code&gt;sort&lt;/code&gt;, not what it contains.&lt;/li&gt;
&lt;li&gt;Status allowlist: checks &lt;code&gt;status&lt;/code&gt; only.&lt;/li&gt;
&lt;li&gt;Parameterised query: binds &lt;code&gt;userId&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt;; identifiers can't be bound.&lt;/li&gt;
&lt;li&gt;Sort allowlist: &lt;strong&gt;none&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Severity:&lt;/strong&gt;  High = Impact High × Likelihood Medium. &lt;code&gt;ORDER BY&lt;/code&gt; accepts sub-selects, so the row order becomes a yes/no oracle over the whole database, including the admin's API token. Any logged-in user can reach it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Why Semgrep says less:&lt;/strong&gt; the rule's MEDIUM/LOW is generic. It can't see that &lt;code&gt;orderBy&lt;/code&gt; comes straight from the query string.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Suggested fix:&lt;/strong&gt; an allowlist of sort columns, right next to the existing status check:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const SORT_COLUMNS = new Map([['total', 'total'], ['status', 'status'], ['created_at', 'created_at']]);

if (sort !== undefined &amp;amp;&amp;amp; !SORT_COLUMNS.has(sort)) {
  return res.status(400).json({ error: 'invalid sort' });
}
const orders = await orderService.search({ status, orderColumn: SORT_COLUMNS.get(sort) }, req.user);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The part I care about most is &lt;strong&gt;Evidence (Manual Reproduction)&lt;/strong&gt;: the steps to reach the same conclusion myself, without trusting the AI.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read the sink: &lt;code&gt;src/repositories/orderRepository.js:7-11&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Find who calls it: &lt;code&gt;grep -rn &quot;orderRepository.find&quot; src&lt;/code&gt; → &lt;code&gt;orderService.js:4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Find where &lt;code&gt;orderColumn&lt;/code&gt; is set: &lt;code&gt;grep -rn &quot;orderColumn&quot; src&lt;/code&gt; → &lt;code&gt;ordersController.js:11&lt;/code&gt;, &lt;code&gt;orderColumn: sort&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Find where &lt;code&gt;sort&lt;/code&gt; comes from: &lt;code&gt;ordersController.js:6&lt;/code&gt;, &lt;code&gt;req.query&lt;/code&gt;. The only check (&lt;code&gt;:7&lt;/code&gt;) is on &lt;code&gt;status&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Find who can send it: &lt;code&gt;routes/orders.js:5&lt;/code&gt;, &lt;code&gt;requireAuth&lt;/code&gt; only.&lt;/li&gt;
&lt;li&gt;Confirm locally: &lt;code&gt;?sort=total&lt;/code&gt; → 200 sorted by total; &lt;code&gt;?sort=price&lt;/code&gt; → 500, and the server log shows &lt;code&gt;column &quot;price&quot; does not exist&lt;/code&gt;. The value reached the SQL text.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And for the false positive, the report doesn't just dismiss it. It says &lt;strong&gt;when it would become real&lt;/strong&gt;, and what to do with the rule:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Would become a true positive if:&lt;/strong&gt; &lt;code&gt;tableName&lt;/code&gt; were ever taken from the request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rule decision:&lt;/strong&gt; keep the rule enabled (it found a real bug in F1) and suppress this one instance with a comment that points to the triage.&lt;/li&gt;
&lt;/ul&gt;
&amp;lt;!--  OPTIONAL SCREENSHOT PLACEHOLDER: the rendered triage report (docs/triage/semgrep-triage.md or the
     Obsidian note) showing the F1 header, &quot;Controls on the path&quot;, and the collapsed
     &quot;Evidence (Manual Reproduction)&quot; callout expanded. --&amp;gt;
&lt;p&gt;The template does two things. It makes the AI follow the steps I would follow. (Without it, a model will often give a verdict, then admit &lt;em&gt;&quot;actually I didn't check X&quot;&lt;/em&gt; when you push.) And it teaches me the pattern every time I read a report. If you'd like to test yourself first, run it with &lt;code&gt;--learn&lt;/code&gt;: you give your own verdict, then compare.&lt;/p&gt;
&lt;h4 id=&quot;4-prove-an-experiment-instead-of-trust&quot;&gt;4.  Prove: an experiment instead of trust&lt;/h4&gt;
&lt;p&gt;Now the XSS finding. The template prints each comment unescaped:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ejs&quot;&gt;&amp;lt;!-- src/views/comments.ejs:18 --&amp;gt;
&amp;lt;div&amp;gt;&amp;lt;%- comment.html %&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;%-&lt;/code&gt; doesn't escape HTML, so Semgrep flags it. The triage traced user input from &lt;code&gt;POST /comments&lt;/code&gt; to this line, and found one control on the way (&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mohamed-osama-aboelkheir/ai-sast-triage-demo/blob/main/src/utils/markdown.js#L4&quot;&gt;source&lt;/a&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// src/utils/markdown.js
exports.render = (text) =&amp;gt; DOMPurify.sanitize(marked.parse(text));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/4kjvp3035ixch8w7gj9a.png&quot; alt=&quot;Source-to-sink trace for the comment body: stored, read back, Markdown → HTML, then DOMPurify () before the template.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Verdict: false positive, because DOMPurify removes the payload. But say I've never used DOMPurify. &lt;strong&gt;Why&lt;/strong&gt; is it enough? I could read the docs. Or I could ask AI to build me a small experiment I can run myself:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Create an &lt;code&gt;/appsec-skills:security-experiment&lt;/code&gt; to explain how DOMPurify sanitize mitigates XSS with a simple example.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;security-experiment&lt;/code&gt; skill writes a Jupyter notebook (a Deno kernel, with the app's real library versions and its real template). It sends one classic payload through the app's steps, one cell per step, first &lt;strong&gt;without&lt;/strong&gt; DOMPurify and then &lt;strong&gt;with&lt;/strong&gt; it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;── Without DOMPurify ──
▶ marked.parse(text)
  &amp;lt;p&amp;gt;Package arrived damaged &amp;lt;img src=&quot;x&quot; onerror=&quot;alert('XSS')&quot;&amp;gt;&amp;lt;/p&amp;gt;
  ⚠️  dangerous: onerror=&quot;alert('XSS')&quot;

── With DOMPurify ──
▶ DOMPurify.sanitize(html)
  &amp;lt;p&amp;gt;Package arrived damaged &amp;lt;img src=&quot;x&quot;&amp;gt;&amp;lt;/p&amp;gt;
  ✅ nothing dangerous left

▶ what DOMPurify removed
  ✂️  onerror=&quot;alert('XSS')&quot; from &amp;lt;img&amp;gt;: event handlers are not on its allowlist
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/lvhd173tald5mbw4rmyf.png&quot; alt=&quot;The DOMPurify experiment running in VS Code: the onerror handler is removed at the sanitize step.&quot;&gt;&lt;/p&gt;
&lt;p&gt;The notebook ends with &lt;strong&gt;why that's enough&lt;/strong&gt; (it runs on every render, after &lt;code&gt;marked&lt;/code&gt;, as the last step before &lt;code&gt;&amp;lt;%-&lt;/code&gt;) and &lt;strong&gt;what would break it&lt;/strong&gt; (removing it, moving it to input only, changing the HTML after sanitising, or an outdated version with a known bypass). I saw the control work. I didn't just take its word for it.&lt;/p&gt;
&lt;h3 id=&quot;-same-verdicts-very-different-engineer&quot;&gt;✅ Same verdicts, very different engineer&lt;/h3&gt;
&lt;p&gt;After both approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Approach 1&lt;/strong&gt; gave me verdicts in two minutes. I couldn't explain them, and the next similar finding would take just as long, because I learned nothing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Approach 2&lt;/strong&gt; gave me the &lt;strong&gt;same verdicts&lt;/strong&gt; with a few short prompts, plus:&lt;ul&gt;
&lt;li&gt;diagrams and tours of how the feature actually works;&lt;/li&gt;
&lt;li&gt;a trace showing exactly why two identical-looking findings differ;&lt;/li&gt;
&lt;li&gt;a report with reproduction steps and a tested fix I can hand to the engineer;&lt;/li&gt;
&lt;li&gt;an experiment that taught me how DOMPurify works.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I can now go to the engineering team with a priority I can stand behind: fix the &lt;code&gt;ORDER BY&lt;/code&gt; injection now; the table-name pattern is a low-priority hardening task.&lt;/p&gt;
&lt;h3 id=&quot;takeaway&quot;&gt;Takeaway&lt;/h3&gt;
&lt;p&gt;None of these techniques are specific to security. Wherever you use AI, you can ask it to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Visualise:&lt;/strong&gt; turn code into a diagram and a guided tour, so you read it as a story;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Trace:&lt;/strong&gt; follow one value through the layers instead of trusting a summary;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Template:&lt;/strong&gt; make it fill in the fields you would check yourself, with evidence;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Experiment:&lt;/strong&gt; build something small you can run, to see the claim hold;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quiz you:&lt;/strong&gt; give your own answer first, then compare (&lt;code&gt;--learn&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI is a powerful tool, and using it does make you more efficient. Just keep an eye on the cognitive debt. Balance &lt;strong&gt;doing&lt;/strong&gt; and &lt;strong&gt;understanding&lt;/strong&gt;, so you're faster now &lt;em&gt;and&lt;/em&gt; not stuck later.&lt;/p&gt;
&lt;h3 id=&quot;try-it-yourself&quot;&gt;Try it yourself&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Demo app (deliberately vulnerable) and every AI output from this post: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mohamed-osama-aboelkheir/ai-sast-triage-demo&quot;&gt;ai-sast-triage-demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The skills: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mohamed-osama-aboelkheir/appsec-untangled-resources/tree/main/plugins/appsec-skills/skills&quot;&gt;appsec-skills&lt;/a&gt; (&lt;code&gt;code-walkthrough&lt;/code&gt;, &lt;code&gt;source-to-sink&lt;/code&gt;, &lt;code&gt;semgrep-triage&lt;/code&gt;, &lt;code&gt;security-experiment&lt;/code&gt;). PRs are welcome.&lt;/li&gt;
&lt;li&gt;More on YouTube: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/@AppSecUntangled&quot;&gt;AppSec Untangled&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How do &lt;strong&gt;you&lt;/strong&gt; use AI to learn and not just to finish tasks? Tell me in the comments. I'd love to add more ideas to the skills.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30045/use-ai-to-get-smarter-not-lazier</guid>
<pubDate>Tue, 06 Oct 2026 18:40:48 +0000</pubDate>
</item>
<item>
<title>The Forgetting Curve Everyone Cites Is Ebbinghaus's Own Column, Relabeled. The 90% Is Not in It.</title>
<link>https://coderlegion.com/30006/the-forgetting-curve-everyone-cites-is-ebbinghauss-own-column-relabeled-the-90-is-not-in</link>
<description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/the-forgetting-curve-everyone-cites-is-ebbinghaus-s-own-column-relabeled-the-90-is-not-in-it/&quot;&gt;vibeagentmaking.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The &quot;half in an hour, 70% in a day&quot; figures that training blogs attribute to Ebbinghaus are his own column IV, a share of relearning time for lists of nonsense syllables, with the unit dropped. The 90% is not in his table; an exponential fits it five times worse than his 1885 formula; two replications confirm the shape.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;On June 8, 2026, MeltingSpot's blog published a long post on the cost of forgetting in corporate training. Its first section, &quot;What the Ebbinghaus forgetting curve actually tells us,&quot; reports what Hermann Ebbinghaus found: &quot;roughly 56% is forgotten within one hour of learning, about 66% within one day, and around 75% within six days.&quot; Those figures are close to numbers Ebbinghaus printed. Two paragraphs later, in the same section, the post says that if a team finishes a Monday morning training and gets no review for the rest of the week, &quot;cognitive science predicts that 90% of the material will be gone by Friday.&quot;&lt;/p&gt;
&lt;p&gt;So 75% at six days became 90% at four, two paragraphs apart. The post then puts a price on it. Citing the Association for Talent Development's estimate that US organizations spend an average of $1,254 per employee per year on training, it says: &quot;If 90% of that training content is not retained past the first week, the effective return on investment is not just low. It is catastrophic.&quot; The worked figure that follows is &quot;roughly $940&quot; wasted per employee. That is 75% of $1,254, the six-day number the post gave in its first section. Ninety percent would be $1,129.&lt;/p&gt;
&lt;p&gt;The same figures run through the training industry's explainers. TalentLMS's blog says Ebbinghaus &quot;found that within an hour of learning new information people tend to forget up to 50% of it. Within 24 hours, this can increase to 70%. By the end of the week, people tend to retain only about 25% of what they've learned.&quot; Further down, citing &quot;studies&quot; that illustrate the curve, it says people &quot;tend to forget up to 90% of what they've learned within a month.&quot; The post dates from March 2023; the page was last modified on September 21, 2026. Today's Class says &quot;on average we forget 50% of new information within an hour, and about 70% within one day,&quot; and &quot;Within 30 days, we forget up to 90% of what we learned.&quot; TrainMeUK's headline from December 2025 is &quot;Why Employees Forget 70% of Training in 24 Hours (The Ebbinghaus Effect),&quot; and its introduction calls the forgetting &quot;a biology problem.&quot;&lt;/p&gt;
&lt;p&gt;We published a version of it too. An essay on this site in April said: &quot;Hermann Ebbinghaus established the lower bound in 1885. Without reinforcement, humans lose roughly half of newly learned information within an hour and most of it within a week.&quot; This piece corrects that sentence, and the one after it, which went further.&lt;/p&gt;
&lt;h3 id=&quot;what-he-measured&quot;&gt;What he measured&lt;/h3&gt;
&lt;p&gt;Ebbinghaus was his own and only subject. The seventh chapter of his book &lt;em&gt;Memory&lt;/em&gt;, in the 1913 English translation by Henry Ruger and Clara Bussenius, gives the design: &quot;The investigations in question fell in the year 1879-80 and comprised 163 double tests.&quot; Each double test meant learning eight series of 13 nonsense syllables (six, in 38 of the tests) until he could recite each series twice without error, then relearning them to the same standard after a fixed interval. He was 29.&lt;/p&gt;
&lt;p&gt;He did not count the syllables he could still recall. He timed the relearning and took the time saved, as a share of the original learning time, as his measure: &quot;This saving in work is each time the measure for the amount remembered at the end of the interval.&quot; If relearning a list took 60% as long as learning it, the saving was 40%.&lt;/p&gt;
&lt;p&gt;He was also careful about whom the results described. Early in his discussion of them he writes that &quot;it would be just as reasonable to be surprised at this initial rapidity and later slowness as they come to light here under the definite conditions of our experiment for a certain individual, and for a series of 13 syllables. One hour after the end of the learning, the forgetting had already progressed so far that one half the amount of the original work had to be expended before the series could be reproduced again; after 8 hours the work to be made up amounted to two thirds of the first effort.&quot;&lt;/p&gt;
&lt;p&gt;That is where &quot;half in an hour&quot; comes from. Its unit is work.&lt;/p&gt;
&lt;h3 id=&quot;the-table&quot;&gt;The table&lt;/h3&gt;
&lt;p&gt;The summary table that closes Section 28 of the chapter has seven rows. Its second column is headed &quot;So much of the series learned was retained that in relearning a saving of Q% of the time of original learning was made.&quot; Its fourth column is headed &quot;The amount forgotten was thus equivalent to v% of the original in terms of time of learning,&quot; and every entry in it is 100 minus the second. The formula table in Section 29 gives the intervals in minutes, and they are less round than the summary suggests: the &quot;1 hour&quot; row was 64 minutes, and the &quot;8.8 hours&quot; row was 526.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Interval &lt;/th&gt;&lt;th&gt;Saving (column II) &lt;/th&gt;&lt;th&gt;Amount forgotten &quot;in terms of time of learning&quot; (column IV)&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;20 minutes &lt;/td&gt;&lt;td&gt;58.2% &lt;/td&gt;&lt;td&gt;41.8%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;64 minutes &lt;/td&gt;&lt;td&gt;44.2% &lt;/td&gt;&lt;td&gt;55.8%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;8.8 hours &lt;/td&gt;&lt;td&gt;35.8% &lt;/td&gt;&lt;td&gt;64.2%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1 day &lt;/td&gt;&lt;td&gt;33.7% &lt;/td&gt;&lt;td&gt;66.3%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2 days &lt;/td&gt;&lt;td&gt;27.8% &lt;/td&gt;&lt;td&gt;72.2%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6 days &lt;/td&gt;&lt;td&gt;25.4% &lt;/td&gt;&lt;td&gt;74.6%&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;31 days &lt;/td&gt;&lt;td&gt;21.1% &lt;/td&gt;&lt;td&gt;78.9%&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Set the retellings beside it and most of their numbers turn out to be his. &quot;Half in an hour&quot; is 55.8% in column IV. &quot;Seventy percent in a day&quot; is 66.3%, rounded up. MeltingSpot's 56, 66 and 75 are column IV to the nearest point. TalentLMS's &quot;about 25%&quot; retained by the end of the week is column II at six days, 25.4%, copied correctly. Ebbinghaus made the conversion from savings to &quot;forgotten&quot; himself, in print.&lt;/p&gt;
&lt;p&gt;What changed on the way is the thing the percentage is a percentage of. Column IV is the share of the original learning time that had to be spent again to relearn a list of nonsense syllables. The retellings make it a share of &quot;new information,&quot; of &quot;training content,&quot; of &quot;what they've learned.&quot; A saving of 44.2% after an hour does not mean 55.8% of anything was lost. It means relearning took 55.8% as long as learning had.&lt;/p&gt;
&lt;p&gt;Tom McDowall's critique in his Instructional Design Tips newsletter (November 2025) makes the savings point well, and says &quot;you cannot legitimately convert savings scores into 'percentage forgotten.'&quot; Ebbinghaus did convert them. He printed the result as column IV, with the unit in the heading. What later writers dropped was the unit.&lt;/p&gt;
&lt;p&gt;The 90% has no source in the table. The largest value in column IV is 78.9%, at 31 days. At six days it is 74.6%. MeltingSpot's Friday, four days after a Monday session, falls between the two-day row (72.2%) and the six-day row (74.6%). TrainMeUK's &quot;80% within a month&quot; is near his 78.9%. The &quot;up to 90%&quot; within a month on TalentLMS and Today's Class is 11 points past it.&lt;/p&gt;
&lt;h3 id=&quot;the-shape&quot;&gt;The shape&lt;/h3&gt;
&lt;p&gt;The retellings also name a shape. MeltingSpot: &quot;memory of new material follows an exponential decay curve.&quot; Today's Class: &quot;the percentage of information retained declined exponentially over time without reinforcement.&quot; McDowall's critique calls it &quot;Ebbinghaus's exponential decline curve&quot; too. An exponential loses a fixed fraction of what remains in each equal stretch of time.&lt;/p&gt;
&lt;p&gt;We fitted that shape and two others to his seven savings values by least squares, with time in minutes. His own formula from 1885, with constants he set &quot;with merely approximate estimates, not involving exact calculation by the method of least squares,&quot; misses his data by 1.73 percentage points, root mean square. A power law misses by 1.91. The best exponential misses by 9.10, more than five times the error of his hand-set formula.&lt;/p&gt;
&lt;p&gt;The exponential cannot follow a curve that falls fast and then nearly stops. The best one starts at 40% saved and stays close to it through the first day: 39.9% at 20 minutes, where he measured 58.2%, and 38.8% at one day, where he measured 33.7%. It is too high at a week (32.4%, against 24.5% from his formula) and too low at a month (16.3%, against 21.3%). His own numbers drop 24.5 points between 20 minutes and one day, and only 4.3 points between the sixth day and the thirty-first. He named the pattern himself: &quot;this initial rapidity and later slowness.&quot;&lt;/p&gt;
&lt;p&gt;A 1996 survey points the same way. David Rubin and Amy Wenzel assembled 210 published data sets and fitted each &quot;to 105 different 2-parameter functions.&quot; Their summary: &quot;The best fits were to the logarithmic function, the power function, the exponential in the square root of time, and the hyperbola in the square root of time.&quot; The plain exponential is not among them.&lt;/p&gt;
&lt;h3 id=&quot;three-people-who-ran-it-again&quot;&gt;Three people who ran it again&lt;/h3&gt;
&lt;p&gt;One subject invites the obvious question, whether his memory was unusual. In 2015 Jaap Murre and Joeri Dros of the University of Amsterdam repeated the experiment with Dros, then 22, as the only subject. &quot;One subject spent 70 hours learning lists and relearning them,&quot; at Ebbinghaus's intervals, and &quot;The results are similar to Ebbinghaus' original data.&quot; They set their results beside a German replication from 1991 by Heller, Mack and Seitz, with two subjects, which they describe as published only in German, without an English abstract, and never cited in international journals in English.&lt;/p&gt;
&lt;p&gt;Their comparison table, in savings:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Interval &lt;/th&gt;&lt;th&gt;Ebbinghaus (1879–80) &lt;/th&gt;&lt;th&gt;Mack (1991) &lt;/th&gt;&lt;th&gt;Seitz (1991) &lt;/th&gt;&lt;th&gt;Dros (2015)&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;20 minutes &lt;/td&gt;&lt;td&gt;0.582 &lt;/td&gt;&lt;td&gt;0.544 &lt;/td&gt;&lt;td&gt;0.442 &lt;/td&gt;&lt;td&gt;0.472&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1 hour &lt;/td&gt;&lt;td&gt;0.442 &lt;/td&gt;&lt;td&gt;0.432 &lt;/td&gt;&lt;td&gt;0.325 &lt;/td&gt;&lt;td&gt;0.373&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;9 hours &lt;/td&gt;&lt;td&gt;0.358 &lt;/td&gt;&lt;td&gt;0.285 &lt;/td&gt;&lt;td&gt;0.270 &lt;/td&gt;&lt;td&gt;0.276&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1 day &lt;/td&gt;&lt;td&gt;0.337 &lt;/td&gt;&lt;td&gt;0.316 &lt;/td&gt;&lt;td&gt;0.270 &lt;/td&gt;&lt;td&gt;0.317&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2 days &lt;/td&gt;&lt;td&gt;0.278 &lt;/td&gt;&lt;td&gt;0.365 &lt;/td&gt;&lt;td&gt;0.286 &lt;/td&gt;&lt;td&gt;0.230&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6 days &lt;/td&gt;&lt;td&gt;0.254 &lt;/td&gt;&lt;td&gt;0.309 &lt;/td&gt;&lt;td&gt;0.205 &lt;/td&gt;&lt;td&gt;0.168&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;31 days &lt;/td&gt;&lt;td&gt;0.211 &lt;/td&gt;&lt;td&gt;0.258 &lt;/td&gt;&lt;td&gt;0.201 &lt;/td&gt;&lt;td&gt;0.041&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The shape holds for all four: a fast early drop, then a long shallow slope. At six days the largest loss in column-IV terms is Dros's, 83.2%. At 31 days, Mack is at 74.2% and Seitz at 79.9%, on either side of Ebbinghaus's 78.9%.&lt;/p&gt;
&lt;p&gt;Dros at 31 days is the one number here that cuts against this essay. His saving of 0.041 is 95.9% in column-IV terms, past 90%. The authors treat it with care. His learning time rose by an average of 2.67 seconds a day per list over the 75 days of the experiment, which mostly affects the longest interval, and with a correction for that drift &quot;the corrected savings measure would be 0.137 for the 31 day interval instead of 0.0410,&quot; which is 86.3%. That, they add, &quot;is still well below the values for the three others.&quot; The Dros column above matches savings computed from the repetition counts in the paper's Table 1 at all seven intervals; computed from the seconds in its list-by-list Table 2, the 31-day saving is 0.090, or 91.0%. So there is a 90% in this literature: one subject, at one month, before the authors' correction for drift.&lt;/p&gt;
&lt;p&gt;The replication also found something Ebbinghaus nearly discarded. In three of the four columns the one-day saving is at least as high as the nine-hour saving, which a curve that only runs downhill does not allow. The authors conclude that the curve &quot;is not completely smooth but most probably shows a jump upwards starting at the 24 hour data point,&quot; and point to sleep: &quot;The current body of research on sleep and memory would predict such a boost after one or two nights and attribute it to sleep, though for this particular type of experiments this has to established more firmly in further experiments.&quot; In Ebbinghaus's own table the one-day value sits 3.3 points above his formula, the largest miss of the seven, and he distrusted it. He wrote that it &quot;would fit in well with the other observations to consider the number 33.7 per cent for the relearning after 24 hours as somewhat too large,&quot; and then that &quot;it is upheld by observations to be stated presently, so that I am in doubt about it.&quot; He left it in.&lt;/p&gt;
&lt;h3 id=&quot;what-a-saving-is&quot;&gt;What a saving is&lt;/h3&gt;
&lt;p&gt;The unit matters even though the shape replicates, because a saving and a recall score are different quantities. Murre returned to the measure in 2023 with Antonio Chessa. Ebbinghaus, they write, &quot;measured memory retention in terms of the learning time saved in subsequent study trials relative to the time spent on the first learning trial,&quot; and they &quot;prove mathematically that Ebbinghaus' savings measure is independent of initial encoding strength, learning time, and relearning times.&quot; They contrast it &quot;with often used forgetting functions based on recall probability.&quot; A saving says how much faster the second learning went. It does not say what share of the material could still be produced on demand, which is what every retelling above takes it to say. And the material was lists of nonsense syllables.&lt;/p&gt;
&lt;h3 id=&quot;what-decades-look-like&quot;&gt;What decades look like&lt;/h3&gt;
&lt;p&gt;Material that means something runs on a different clock. Harry Bahrick tested 733 people on the Spanish they had learned in school, across a span of 50 years, and published the results in 1984. &quot;The analysis yields memory curves which decline exponentially for the first 3-6 years of the retention interval. After that retention remains unchanged for periods of up to 30 years before showing a final decline.&quot; Most had not kept it up: &quot;The great majority of subjects rehearse so little that the data reveal no significant rehearsal effects.&quot; Retention over the whole period was &quot;predictable on the basis of the level of original training,&quot; and &quot;Large portions of the originally acquired information remain accessible for over 50 years in spite of the fact the information is not used or rehearsed.&quot;&lt;/p&gt;
&lt;p&gt;Bahrick measured something different again, with tests of reading comprehension, recall and recognition, so his curves neither confirm nor overturn Ebbinghaus's. They show how far a table about nonsense syllables sits from a language learned in school.&lt;/p&gt;
&lt;h3 id=&quot;our-april-sentence&quot;&gt;Our April sentence&lt;/h3&gt;
&lt;p&gt;Here is the passage we published on April 26, 2026:&lt;/p&gt;
&lt;p&gt;Hermann Ebbinghaus established the lower bound in 1885. Without reinforcement, humans lose roughly half of newly learned information within an hour and most of it within a week. Modern memory research has refined the curve in countless ways, but the directional finding has held up across 140 years of experiment. &quot;We trained the team on the policy last quarter&quot; is, neurologically, close to indistinguishable from &quot;we never trained them.&quot;&lt;/p&gt;
&lt;p&gt;The direction holds: both replications above found the fast early drop and the long flattening. The rest does not. &quot;Humans&quot; was one man, and he said so. &quot;Newly learned information&quot; was the time needed to relearn lists of nonsense syllables. &quot;Most of it within a week&quot; is true only in his unit, where it was 74.6% at six days. The chapter the numbers come from reports an average curve for one person and describes no lower bound. The last sentence is contradicted by the table it leans on: a month after learning, relearning still took 21.1% less time than learning had. Nothing in the experiment was neurological either; what he measured was time. And Bahrick's former students, most of whom rehearsed too little for it to show in the data, kept large parts of their Spanish for decades.&lt;/p&gt;
&lt;p&gt;What the passage should have said: In 1879 and 1880, Hermann Ebbinghaus timed himself learning and relearning lists of nonsense syllables. An hour after learning a list, relearning it took about 56% as long as learning it had; a month later, about 79%. Replications in 1991 and 2015 found curves of the same shape.&lt;/p&gt;
&lt;h3 id=&quot;measuring-your-own&quot;&gt;Measuring your own&lt;/h3&gt;
&lt;p&gt;The part of Ebbinghaus a trainer can use is the method, and it needs a stopwatch. Time how long a group takes to reach a standard the first time: a procedure done without error, or a test passed. Some weeks later, before any refresher, time how long the same group takes to reach the same standard again. The time saved, as a share of the first session, is his measure for your material and your people. If the second session takes 70% as long as the first, the saving is 30%, and it is a number about your training.&lt;/p&gt;
&lt;p&gt;Anyone who still wants to quote the 1885 figures can, with the qualifier Ebbinghaus attached to them: &quot;for a certain individual, and for a series of 13 syllables.&quot;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;The curve fits come from &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/code/c6733/refit_ebbinghaus_c6733.py&quot;&gt;refit_ebbinghaus_c6733.py&lt;/a&gt;, which fits three shapes to the seven savings values in Ebbinghaus's summary table by least squares. Its first check is that his formula, as coded, reproduces the calculated values he printed in his formula table; it does, to within 0.1 point, but only with the exact 64 minutes from that table in place of a rounded hour, and that check is how the 64 minutes surfaced. Every quotation and figure above is checked against saved copies of its source by &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/code/c6733/figures_c6733.py&quot;&gt;figures_c6733.py&lt;/a&gt;, which also recomputes the column-IV equivalents, the MeltingSpot arithmetic and the Murre and Dros savings from their Tables 1 and 2. Both scripts and their output are in &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/code/c6733/&quot;&gt;one folder&lt;/a&gt;, with a list of every saved source the check reads, its public address, when it was saved and a checksum of the saved copy; the pages themselves are linked in the Sources below rather than republished. Ebbinghaus's tables are scanned images in the online edition; the values above were read off the scans, and his savings column agrees with Murre and Dros's transcription to the digit.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Part of &lt;strong&gt;Where the Number Came From&lt;/strong&gt;, on how a published number is a fact about the way it was measured: &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/stanford-says-12-to-66-but-12-of-what/&quot;&gt;Stanford says 12% to 66%, but 12% of what?&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/the-safety-score-is-a-fact-about-the-test-rig/&quot;&gt;The safety score is a fact about the test rig&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/95-80-42-tracing-the-2026-ai-failure-statistics/&quot;&gt;Tracing the 2026 AI-failure statistics to a primary&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Hermann Ebbinghaus, &lt;em&gt;Memory: A Contribution to Experimental Psychology&lt;/em&gt; (1885), translated by Henry A. Ruger and Clara E. Bussenius (Teachers College, Columbia University, 1913), Chapter VII: Section 28 (the design; the definition of Q; the summary table that closes the section) and Section 29 (paragraphs 1 to 3, with the formula table of observed and calculated values). Classics in the History of Psychology, York University. &lt;a rel=&quot;nofollow&quot; href=&quot;https://psychclassics.yorku.ca/Ebbinghaus/memory7.htm&quot;&gt;https://psychclassics.yorku.ca/Ebbinghaus/memory7.htm&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jaap M. J. Murre and Joeri Dros, &quot;Replication and Analysis of Ebbinghaus' Forgetting Curve,&quot; &lt;em&gt;PLOS ONE&lt;/em&gt; 10(7): e0120644 (2015): abstract; Methods (the intervals, the subject, the differences from Ebbinghaus and from Heller et al.); Tables 1, 2 and 3; the paragraph after Table 3 (the drift correction); Discussion. &lt;a rel=&quot;nofollow&quot; href=&quot;https://doi.org/10.1371/journal.pone.0120644&quot;&gt;https://doi.org/10.1371/journal.pone.0120644&lt;/a&gt; (data: &lt;a rel=&quot;nofollow&quot; href=&quot;https://osf.io/6kfrp/&quot;&gt;https://osf.io/6kfrp/&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;O. Heller, W. Mack and J. Seitz, &quot;Replikation der Ebbinghaus'schen Vergessenskurve mit der Ersparnismethode: 'Das Behalten und Vergessen als Function der Zeit',&quot; &lt;em&gt;Zeitschrift für Psychologie&lt;/em&gt; 199 (1991): 3–18. Values as reported in Murre and Dros, Table 3; the paper itself was not read.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jaap M. J. Murre and Antonio G. Chessa, &quot;Why Ebbinghaus' savings method from 1885 is a very 'pure' measure of memory performance,&quot; &lt;em&gt;Psychonomic Bulletin &amp;amp; Review&lt;/em&gt; 30(1) (2023): 303–307, abstract. &lt;a rel=&quot;nofollow&quot; href=&quot;https://doi.org/10.3758/s13423-022-02172-3&quot;&gt;https://doi.org/10.3758/s13423-022-02172-3&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Harry P. Bahrick, &quot;Semantic memory content in permastore: Fifty years of memory for Spanish learned in school,&quot; &lt;em&gt;Journal of Experimental Psychology: General&lt;/em&gt; 113(1) (1984): 1–29, abstract. &lt;a rel=&quot;nofollow&quot; href=&quot;https://doi.org/10.1037/0096-3445.113.1.1&quot;&gt;https://doi.org/10.1037/0096-3445.113.1.1&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;David C. Rubin and Amy E. Wenzel, &quot;One hundred years of forgetting: A quantitative description of retention,&quot; &lt;em&gt;Psychological Review&lt;/em&gt; 103(4) (1996): 734–760, abstract (p. 734). &lt;a rel=&quot;nofollow&quot; href=&quot;https://doi.org/10.1037/0033-295X.103.4.734&quot;&gt;https://doi.org/10.1037/0033-295X.103.4.734&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Benoit Chatelier, &quot;Forgetting curve corporate training: the real cost,&quot; MeltingSpot blog, June 8, 2026: the sections &quot;What the Ebbinghaus forgetting curve actually tells us&quot; and &quot;The real financial cost of forgetting in L&amp;amp;D,&quot; and the paragraph before the second. &lt;a rel=&quot;nofollow&quot; href=&quot;https://meltingspot.io/en/blog/forgetting-curve-corporate-training&quot;&gt;https://meltingspot.io/en/blog/forgetting-curve-corporate-training&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Athena Marousis, &quot;What is the Ebbinghaus forgetting curve? Examples and strategies for overcoming it,&quot; TalentLMS Blog, March 30, 2023 (modified September 21, 2026): &quot;What is the forgetting curve?&quot; and the section after it. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.talentlms.com/blog/ebbinghaus-forgetting-curve/&quot;&gt;https://www.talentlms.com/blog/ebbinghaus-forgetting-curve/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;David Boyes, &quot;Fighting the Forgetting Curve,&quot; Today's Class blog, May 5, 2023 (modified April 28, 2025): the sections &quot;What Is the 'Forgetting Curve'?&quot; and &quot;How Much Time Has Passed.&quot; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.todaysclass.com/blog/fighting-the-forgetting-curve&quot;&gt;https://www.todaysclass.com/blog/fighting-the-forgetting-curve&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TrainMeUK, &quot;Why Employees Forget 70% of Training in 24 Hours (The Ebbinghaus Effect),&quot; December 3, 2025: headline and introduction. &lt;a rel=&quot;nofollow&quot; href=&quot;https://trainmeuk.co.uk/resources/ebbinghaus-effect-employees-forget-training-24-hours&quot;&gt;https://trainmeuk.co.uk/resources/ebbinghaus-effect-employees-forget-training-24-hours&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tom McDowall, &quot;Sunday L&amp;amp;D Myth #4: People Forget 70% of What They Learn Within 24 Hours,&quot; Instructional Design Tips, November 9, 2025. &lt;a rel=&quot;nofollow&quot; href=&quot;https://idtips.substack.com/p/sunday-l-and-d-myth-4-people-forget&quot;&gt;https://idtips.substack.com/p/sunday-l-and-d-myth-4-people-forget&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&quot;We Wrote 25 Reminders and Made the Same Mistake Every Time,&quot; this site, April 26, 2026: the paragraph beginning &quot;Hermann Ebbinghaus established the lower bound.&quot; &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/blog/we-wrote-25-reminders-and-made-the-same-mistake-every-time/&quot;&gt;https://vibeagentmaking.com/blog/we-wrote-25-reminders-and-made-the-same-mistake-every-time/&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;keep-the-trail-back-to-where-the-number-was-measured-&quot;&gt;Keep the trail back to where the number was measured.&lt;/h4&gt;
&lt;p&gt;Every number in this piece survived because a script checks it against saved copies of its sources, so the trail from the figure back to the page it was measured on stays intact. A statistic that loses that trail, as &quot;half in an hour&quot; lost its unit, keeps its digits and changes its meaning. If you run agents that report numbers, keep the trail: Chain of Consciousness keeps a tamper-evident record of an agent's actions, so a figure it published can be traced back to what it actually read.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install chain-of-consciousness
npm install chain-of-consciousness
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or start without installing anything: &lt;a rel=&quot;nofollow&quot; href=&quot;https://vibeagentmaking.com/hosted-coc/&quot;&gt;Hosted Chain of Consciousness&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30006/the-forgetting-curve-everyone-cites-is-ebbinghauss-own-column-relabeled-the-90-is-not-in</guid>
<pubDate>Tue, 06 Oct 2026 13:19:27 +0000</pubDate>
</item>
<item>
<title>🚨 CSS Specificity — The Hidden Reason Your UI Breaks</title>
<link>https://coderlegion.com/29269/css-specificity-the-hidden-reason-your-ui-breaks</link>
<description>&lt;p&gt;Most developers learn CSS specificity once.&lt;/p&gt;
&lt;p&gt;They remember:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;#id &amp;gt; .class &amp;gt; div
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then move on.&lt;/p&gt;
&lt;p&gt;Until one day…&lt;/p&gt;
&lt;p&gt;Everything looks correct.&lt;/p&gt;
&lt;p&gt;The CSS is present.&lt;/p&gt;
&lt;p&gt;The selector is correct.&lt;/p&gt;
&lt;p&gt;The z-index looks higher.&lt;/p&gt;
&lt;p&gt;And yet the UI is broken.&lt;/p&gt;
&lt;p&gt;That’s when CSS specificity stops being a beginner topic and becomes a production debugging problem.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-production-incident-that-started-this&quot;&gt;The Production Incident That Started This&lt;/h2&gt;
&lt;p&gt;Recently, I was working on a microfrontend application.&lt;/p&gt;
&lt;p&gt;Everything worked fine initially.&lt;/p&gt;
&lt;p&gt;I opened a page, launched a modal and the UI looked correct.&lt;/p&gt;
&lt;p&gt;Then I navigated to another microfrontend.&lt;/p&gt;
&lt;p&gt;Its CSS got loaded.&lt;/p&gt;
&lt;p&gt;After returning to the original microfrontend, suddenly:&lt;/p&gt;
&lt;p&gt;❌ Modal appeared behind page content&lt;br&gt;
❌ Overlay behaved incorrectly&lt;br&gt;
❌ z-index looked correct but wasn't working&lt;/p&gt;
&lt;p&gt;DevTools showed my CSS rule still existed.&lt;/p&gt;
&lt;p&gt;Yet another CSS rule was winning.&lt;/p&gt;
&lt;p&gt;The culprit?&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;CSS Specificity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-what-is-css-specificity-&quot;&gt;🧠 What is CSS Specificity?&lt;/h2&gt;
&lt;p&gt;CSS specificity is the algorithm browsers use to decide:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Which CSS rule wins when multiple rules target the same element.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Browsers don't simply apply:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;The last CSS rule.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That's one of the biggest misconceptions.&lt;/p&gt;
&lt;p&gt;Specificity is calculated first.&lt;/p&gt;
&lt;p&gt;Only when specificity is equal does source order become important.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-example&quot;&gt;⚔️ Example&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.modal {
  z-index: 9999;
}

.some-library .modal {
  z-index: 100;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&quot;some-library&quot;&amp;gt;
  &amp;lt;div class=&quot;modal&quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Many developers expect:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.modal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to win because the value is larger.&lt;/p&gt;
&lt;p&gt;But CSS doesn't compare values first.&lt;/p&gt;
&lt;p&gt;It compares selectors.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-how-specificity-works&quot;&gt;🧮 How Specificity Works&lt;/h2&gt;
&lt;p&gt;Specificity is usually represented as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;ID - CLASS - TYPE
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;specificity-table&quot;&gt;Specificity Table&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Selector        &lt;/th&gt;&lt;th&gt;Specificity&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;*&lt;/code&gt;             &lt;/td&gt;&lt;td&gt;0-0-0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;div&lt;/code&gt;           &lt;/td&gt;&lt;td&gt;0-0-1&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;.modal&lt;/code&gt;        &lt;/td&gt;&lt;td&gt;0-1-0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;[type=&quot;text&quot;]&lt;/code&gt; &lt;/td&gt;&lt;td&gt;0-1-0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;:hover&lt;/code&gt;        &lt;/td&gt;&lt;td&gt;0-1-0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;#dialog&lt;/code&gt;       &lt;/td&gt;&lt;td&gt;1-0-0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Inline Style    &lt;/td&gt;&lt;td&gt;Highest&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;MDN defines specificity as the weight browsers calculate to determine which declaration gets applied when multiple selectors match the same element.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;example-calculation&quot;&gt;Example Calculation&lt;/h2&gt;
&lt;p&gt;Selector:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;button.primary
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Contains:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;button     → 0-0-1
.primary   → 0-1-0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Total:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;0-1-1
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;Another selector:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;#header button.primary
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Contains:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;#header   → 1-0-0
button    → 0-0-1
.primary  → 0-1-0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Total:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;1-1-1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This selector wins.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-myth-1-last-css-always-wins&quot;&gt;❌ Myth #1 — Last CSS Always Wins&lt;/h2&gt;
&lt;p&gt;Many developers believe:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The CSS rule written last wins.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Not always.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;#header {
  color: red;
}

div {
  color: blue;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div id=&quot;header&quot;&amp;gt;
  Hello
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Result:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Red
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Because:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;#header = 1-0-0
div     = 0-0-1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Specificity wins before source order.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-myth-2-higher-z-index-always-wins&quot;&gt;❌ Myth #2 — Higher z-index Always Wins&lt;/h2&gt;
&lt;p&gt;This causes countless production bugs.&lt;/p&gt;
&lt;p&gt;Developers often write:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.modal {
  z-index: 99999;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And expect it to appear above everything.&lt;/p&gt;
&lt;p&gt;Not necessarily.&lt;/p&gt;
&lt;p&gt;Because:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;z-index works inside stacking contexts.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-what-is-a-stacking-context-&quot;&gt;🧠 What is a Stacking Context?&lt;/h2&gt;
&lt;p&gt;A stacking context is a separate layering environment.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.parent {
  position: relative;
  z-index: 1;
}

.child {
  position: absolute;
  z-index: 999999;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;999999
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the child cannot escape its parent's stacking context.&lt;/p&gt;
&lt;p&gt;This is why modal, tooltip, dropdown, and popover bugs often feel confusing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-microfrontend-problem&quot;&gt;The Microfrontend Problem&lt;/h2&gt;
&lt;p&gt;This is where specificity becomes extremely important.&lt;/p&gt;
&lt;p&gt;Imagine:&lt;/p&gt;
&lt;h4 id=&quot;microfrontend-a&quot;&gt;Microfrontend A&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.modal {
  z-index: 9999;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;microfrontend-b&quot;&gt;Microfrontend B&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.layout .modal {
  z-index: 100;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Specificity:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.modal
0-1-0

.layout .modal
0-2-0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Microfrontend B wins.&lt;/p&gt;
&lt;p&gt;Even if your CSS is still loaded.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-my-fix-worked&quot;&gt;Why My Fix Worked&lt;/h2&gt;
&lt;p&gt;Originally:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.modal {
  z-index: 9999;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But another selector had higher specificity.&lt;/p&gt;
&lt;p&gt;So I wrapped the modal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;&amp;lt;div class=&quot;dialog-wrapper&quot;&amp;gt;
  &amp;lt;Modal /&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.dialog-wrapper .modal {
  z-index: 9999;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.modal
0-1-0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;.dialog-wrapper .modal
0-2-0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now my selector became more specific and started winning.&lt;/p&gt;
&lt;p&gt;The CSS value never changed.&lt;/p&gt;
&lt;p&gt;Only specificity changed.&lt;/p&gt;
&lt;p&gt;And the issue disappeared.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-why-this-happens-more-in-microfrontends&quot;&gt;🚨 Why This Happens More in Microfrontends&lt;/h2&gt;
&lt;p&gt;Microfrontends often share:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Same DOM&lt;/li&gt;
&lt;li&gt;Same global CSS&lt;/li&gt;
&lt;li&gt;Same class names&lt;/li&gt;
&lt;li&gt;Same browser environment&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Problems include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CSS leakage&lt;/li&gt;
&lt;li&gt;Style collisions&lt;/li&gt;
&lt;li&gt;Unexpected overrides&lt;/li&gt;
&lt;li&gt;Load-order changes after navigation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This makes specificity bugs significantly more common.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;modern-css-specificity-features-developers-often-miss&quot;&gt;Modern CSS Specificity Features Developers Often Miss&lt;/h2&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-is-&quot;&gt;1️⃣ &lt;code&gt;:is()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;:is(.btn, .link)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;:is()&lt;/code&gt; pseudo-class itself doesn't add specificity.&lt;/p&gt;
&lt;p&gt;Instead, it takes the specificity of the most specific selector inside it.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;:is(.btn, #header)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Specificity:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;1-0-0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;#header
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is most specific.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-not-&quot;&gt;2️⃣ &lt;code&gt;:not()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Many developers think:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;:not(.hidden)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;adds nothing.&lt;/p&gt;
&lt;p&gt;Not true.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:not()&lt;/code&gt; itself doesn't contribute specificity.&lt;/p&gt;
&lt;p&gt;But selectors inside it do.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;:not(.hidden)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Specificity:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;0-1-0
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-where-&quot;&gt;3️⃣ &lt;code&gt;:where()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;One of the most underrated CSS features.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;:where(.btn)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Specificity:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;0-0-0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Always.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:where()&lt;/code&gt; intentionally has zero specificity.&lt;/p&gt;
&lt;p&gt;This makes it extremely useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Design systems&lt;/li&gt;
&lt;li&gt;Component libraries&lt;/li&gt;
&lt;li&gt;Shared UI frameworks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because consumers can easily override styles.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-to-debug-css-specificity-issues&quot;&gt;How to Debug CSS Specificity Issues&lt;/h2&gt;
&lt;p&gt;When CSS isn't applying:&lt;/p&gt;
&lt;p&gt;Check these in order:&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-is-the-rule-present-&quot;&gt;1. Is the rule present?&lt;/h3&gt;
&lt;p&gt;Open DevTools.&lt;/p&gt;
&lt;p&gt;Verify the CSS exists.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-is-it-crossed-out-&quot;&gt;2. Is it crossed out?&lt;/h3&gt;
&lt;p&gt;Crossed-out CSS usually means:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Another rule won.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;3-which-selector-is-winning-&quot;&gt;3. Which selector is winning?&lt;/h3&gt;
&lt;p&gt;DevTools shows the winning selector.&lt;/p&gt;
&lt;p&gt;Inspect it carefully.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;4-compare-specificity&quot;&gt;4. Compare specificity&lt;/h3&gt;
&lt;p&gt;Many bugs become obvious here.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;5-check-css-load-order&quot;&gt;5. Check CSS load order&lt;/h3&gt;
&lt;p&gt;When specificity is equal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Last rule wins.
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;6-check-stacking-contexts&quot;&gt;6. Check stacking contexts&lt;/h3&gt;
&lt;p&gt;Especially for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modals&lt;/li&gt;
&lt;li&gt;Dropdowns&lt;/li&gt;
&lt;li&gt;Tooltips&lt;/li&gt;
&lt;li&gt;Popovers&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;7-check-portals&quot;&gt;7. Check portals&lt;/h3&gt;
&lt;p&gt;React portals often render outside expected DOM hierarchy.&lt;/p&gt;
&lt;p&gt;This changes how CSS behaves.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-best-practices&quot;&gt;🎯 Best Practices&lt;/h2&gt;
&lt;p&gt;✅ Prefer class selectors over IDs&lt;/p&gt;
&lt;p&gt;✅ Avoid deeply nested selectors&lt;/p&gt;
&lt;p&gt;✅ Use low-specificity CSS in shared libraries&lt;/p&gt;
&lt;p&gt;✅ Use &lt;code&gt;:where()&lt;/code&gt; for easily overridable styles&lt;/p&gt;
&lt;p&gt;✅ Be careful with global CSS in microfrontends&lt;/p&gt;
&lt;p&gt;✅ Avoid excessive &lt;code&gt;!important&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Most developers think CSS specificity is a beginner topic.&lt;/p&gt;
&lt;p&gt;In reality:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;It's one of the most common reasons production UIs break unexpectedly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Especially in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Large applications&lt;/li&gt;
&lt;li&gt;Design systems&lt;/li&gt;
&lt;li&gt;Microfrontends&lt;/li&gt;
&lt;li&gt;Shared component libraries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tricky part is that the CSS often looks correct.&lt;/p&gt;
&lt;p&gt;The rule exists.&lt;/p&gt;
&lt;p&gt;The value is correct.&lt;/p&gt;
&lt;p&gt;And yet another selector silently wins.&lt;/p&gt;
&lt;p&gt;Understanding specificity deeply transforms CSS debugging from:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Trial and Error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;into:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Predictable Engineering
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;key-takeaways&quot;&gt;Key Takeaways&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;CSS does not simply apply the last rule&lt;/li&gt;
&lt;li&gt;Specificity determines which selector wins&lt;/li&gt;
&lt;li&gt;Higher z-index does not always mean higher visibility&lt;/li&gt;
&lt;li&gt;Microfrontends make specificity issues more common&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:is()&lt;/code&gt;, &lt;code&gt;:not()&lt;/code&gt;, and &lt;code&gt;:where()&lt;/code&gt; have special specificity behavior&lt;/li&gt;
&lt;li&gt;&lt;code&gt;!important&lt;/code&gt; should be a last resort&lt;/li&gt;
&lt;li&gt;DevTools can quickly reveal specificity conflicts when used correctly&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Understanding CSS specificity is one of those skills that feels minor — until it saves you hours of debugging a production UI issue.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29269/css-specificity-the-hidden-reason-your-ui-breaks</guid>
<pubDate>Wed, 30 Sep 2026 16:07:21 +0000</pubDate>
</item>
<item>
<title>Arham Sayyed's Cybersecurity Workshop at ITM Skills University: Web Security, DevSecOps &amp; Honeypots</title>
<link>https://coderlegion.com/29148/arham-sayyeds-cybersecurity-workshop-skills-university-security-devsecops-honeypots</link>
<description>&lt;p&gt;Arham Sayyed, a Mumbai-based full-stack developer and security researcher, ran a three-part cybersecurity workshop on 7 September 2026 at ITM Skills University's &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.itm.edu/school-of-futuretech/&quot;&gt;School of Future Tech&lt;/a&gt; in Kharghar, in collaboration with &lt;a rel=&quot;nofollow&quot; href=&quot;https://haxnation.org/&quot;&gt;Haxnation&lt;/a&gt;, for B.Tech students across three academic years. This post is a complete recap of what was actually taught, session by session: the tools used, the vulnerabilities demonstrated live, the exact mechanics behind each exploit, and the hands-on labs students worked through themselves.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Workshop information:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Instructor:&lt;/strong&gt; Arham Sayyed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Organization:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://haxnation.org/&quot;&gt;Haxnation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Venue:&lt;/strong&gt; ITM Skills University's &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.itm.edu/school-of-futuretech/&quot;&gt;School of Future Tech&lt;/a&gt;, Kharghar&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Date:&lt;/strong&gt; 7 September 2026&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Topics:&lt;/strong&gt; Web security, &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;SQL injection&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.wireshark.org&quot;&gt;Wireshark&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/DevSecOps&quot;&gt;DevSecOps&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/CI/CD&quot;&gt;CI/CD&lt;/a&gt; security, &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/actions&quot;&gt;GitHub Actions&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Honeypot_(computing)&quot;&gt;honeypots&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Intrusion_detection_system&quot;&gt;intrusion detection&lt;/a&gt;, detection engineering&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The workshop ran as three separate sessions back to back: one for Second Year B.Tech students, one for Third Year B.Tech students under the Cybersecurity Specialization track, and one for Final Year B.Tech students, also under the Cybersecurity Specialization track. Six hours, three batches, one continuous idea running through all of it: security is something you learn by breaking things yourself, not by watching a slide deck describe someone else breaking them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/g0avwx9ae7t3tvf8r54e.jpg&quot; alt=&quot;Arham Sayyed conducting a cybersecurity workshop at ITM Skills University, Kharghar&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;arham-sayyed-s-cybersecurity-workshop-at-itm-skills-university&quot;&gt;Arham Sayyed's Cybersecurity Workshop at ITM Skills University&lt;/h3&gt;
&lt;p&gt;ITM Skills University's &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.itm.edu/school-of-futuretech/&quot;&gt;School of Future Tech&lt;/a&gt; invited Arham Sayyed to bring a full day of hands-on cybersecurity training to its Kharghar campus, part of a broader push to give engineering students practical exposure to security work well before they graduate into it. Cybersecurity roles increasingly expect candidates to have touched real tools before their first job, not just studied the theory behind them, and a single elective course rarely covers the gap between &quot;understands the &lt;a rel=&quot;nofollow&quot; href=&quot;https://owasp.org/www-project-top-ten/&quot;&gt;OWASP Top 10&lt;/a&gt; as a concept&quot; and &quot;has actually run a &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;SQL injection&lt;/a&gt; payload against a real database.&quot;&lt;/p&gt;
&lt;p&gt;Rather than a single generic talk, the day was structured as three distinct, escalating sessions, each scoped to where a batch actually was in its coursework, and each built around real, working tools rather than slideware standing in for them. Every session followed the same underlying philosophy: show a real exploit live, before any theory, then hand students the exact same environment to attack themselves. No simulations standing in for real vulnerabilities, no toy examples that don't map to anything real. &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/burp&quot;&gt;Burp Suite&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.wireshark.org&quot;&gt;Wireshark&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/actions&quot;&gt;GitHub Actions&lt;/a&gt;, and a hand-built &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Honeypot_(computing)&quot;&gt;honeypot&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Intrusion_detection_system&quot;&gt;intrusion detection&lt;/a&gt; system, all running for real, in front of the room.&lt;/p&gt;
&lt;h4 id=&quot;what-is-the-cybersecurity-specialization-track-&quot;&gt;What Is the Cybersecurity Specialization Track?&lt;/h4&gt;
&lt;p&gt;Third Year and Final Year batches at ITM Skills University's School of Future Tech follow a Cybersecurity Specialization track within their B.Tech program, a focused stream layered on top of the core computer engineering curriculum specifically covering security topics: network defense, application security, governance and compliance, and increasingly, the kind of &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/DevSecOps&quot;&gt;DevSecOps&lt;/a&gt; and detection engineering skills covered in Sessions Two and Three of this workshop. Second Year students, not yet on the specialization track, received the foundational Session One instead, giving every batch content actually matched to their level rather than a single one-size-fits-all talk.&lt;/p&gt;
&lt;h3 id=&quot;session-one-web-security-and-network-analysis&quot;&gt;Session One: Web Security and Network Analysis&lt;/h3&gt;
&lt;p&gt;The first session ran for the Second Year B.Tech batch and covered web application security and network traffic analysis from the ground up, assuming zero prior security background, since this batch had not yet started the specialization track.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/2p4uau19sm43c7v7bw72.jpg&quot; alt=&quot;Cybersecurity workshop session briefing slide displayed on stage at ITM Skills University, Kharghar&quot;&gt;&lt;/p&gt;
&lt;p&gt;Arham Sayyed opened the session the same way a &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Penetration_test&quot;&gt;penetration test&lt;/a&gt; actually starts: a live login bypass, demonstrated before a single slide of theory. From there, the session built out the full picture, starting with the fundamentals almost nobody's coursework actually covers directly: what &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview&quot;&gt;HTTP&lt;/a&gt; is, what a request and response actually contain, and why the protocol has no built-in memory of who you are between one page load and the next.&lt;/p&gt;
&lt;h4 id=&quot;what-a-proxy-is-and-what-burp-suite-actually-does&quot;&gt;What a Proxy Is, and What Burp Suite Actually Does&lt;/h4&gt;
&lt;p&gt;Before naming any specific tool, the session covered what a &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling&quot;&gt;proxy&lt;/a&gt; is in general: software that sits between a browser and a server and passes traffic through itself instead of letting it travel directly. Whoever controls that proxy can read, and critically, edit, traffic before it ever reaches the server it was addressed to.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/burp&quot;&gt;Burp Suite&lt;/a&gt;, made by the UK security company &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net&quot;&gt;PortSwigger&lt;/a&gt;, is a proxy built specifically for that second part, editing traffic on purpose, so a tester can study exactly what happens when a request is tampered with. It has been the default tool of the web application penetration testing industry since roughly the mid-2000s, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/burp/communitydownload&quot;&gt;Burp Suite Community&lt;/a&gt;, the free edition used throughout this session, ships with an embedded &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.chromium.org/Home/&quot;&gt;Chromium&lt;/a&gt; browser already configured to route through the proxy, removing the usual fifteen minutes of manual certificate and proxy configuration that stalls most introductory Burp tutorials.&lt;/p&gt;
&lt;h4 id=&quot;sql-injection&quot;&gt;SQL Injection&lt;/h4&gt;
&lt;p&gt;Students then watched, and later reproduced themselves, a real &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;SQL injection&lt;/a&gt; login bypass. The underlying mistake is almost always the same across real applications: a login check builds its database query by directly pasting submitted input into a string, rather than treating that input strictly as data. Typing &lt;code&gt;' OR '1'='1' --&lt;/code&gt; into an email field, with any password at all, rewrites the query's logic entirely. The &lt;code&gt;OR '1'='1'&lt;/code&gt; clause makes the WHERE condition true regardless of the actual credentials, and the trailing &lt;code&gt;--&lt;/code&gt; comments out the rest of the original query, including the password check itself, so it never runs. The result is a successful login as whichever user the database happens to return first, typically the administrator account.&lt;/p&gt;
&lt;p&gt;Students then worked through a real &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;SQL injection&lt;/a&gt; lab on PortSwigger's own &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security&quot;&gt;Web Security Academy&lt;/a&gt;, the same free, industry-recognized platform used by professionals preparing for the &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/certification&quot;&gt;Burp Suite Certified Practitioner&lt;/a&gt; exam. The specific lab, &quot;&lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/sql-injection/lab-login-bypass&quot;&gt;SQL injection vulnerability allowing login bypass&lt;/a&gt;,&quot; uses the identical technique demonstrated live, in a fully isolated, disposable environment built for exactly this purpose.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/4e9kbvdq213dq8d1yqsx.jpg&quot; alt=&quot;Arham Sayyed demonstrating a live SQL injection lab on PortSwigger Web Security Academy&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;packet-analysis-and-the-real-difference-between-http-and-https&quot;&gt;Packet Analysis and the Real Difference Between HTTP and HTTPS&lt;/h4&gt;
&lt;p&gt;The second half of the session moved into network traffic analysis with &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.wireshark.org&quot;&gt;Wireshark&lt;/a&gt;, free, open-source &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Packet_analyzer&quot;&gt;packet capture&lt;/a&gt; software that has been the default tool taught in networking courses worldwide since it was renamed from Ethereal in 2006. Students were walked through what a packet actually contains, a header describing where it came from and where it is going, and a payload carrying the actual content, before capturing a live login attempt themselves.&lt;/p&gt;
&lt;p&gt;The centerpiece of this half was a live before-and-after demonstration: the exact same login form, submitted with the exact same credentials, once over plain HTTP and once over &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/HTTPS&quot;&gt;HTTPS&lt;/a&gt;. Over HTTP, filtering the capture and following the HTTP stream revealed the submitted username and password in fully readable plain text. Over HTTPS, the same filter on the same login showed nothing but ciphertext, because a &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/&quot;&gt;TLS handshake&lt;/a&gt; had already encrypted the connection before a single byte of the actual form data left the browser. One honest caveat came with it: HTTPS defeats a packet sniffer sitting outside the machine, but does nothing about a proxy like &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/burp&quot;&gt;Burp Suite&lt;/a&gt;, which the browser was explicitly configured to trust.&lt;/p&gt;
&lt;p&gt;Students closed out Session One with hands-on networking challenges on a &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Capture_the_flag_(cybersecurity)&quot;&gt;capture-the-flag&lt;/a&gt; platform, applying the same filter-and-follow-stream technique against real capture files to reinforce the skill in a graded, self-paced environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Topics covered in Session One:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview&quot;&gt;HTTP&lt;/a&gt; fundamentals: requests, responses, and statelessness&lt;/li&gt;
&lt;li&gt;What a &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling&quot;&gt;proxy&lt;/a&gt; is, and why &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/burp&quot;&gt;Burp Suite&lt;/a&gt; is built around that concept&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/burp&quot;&gt;Burp Suite&lt;/a&gt; fundamentals and web exploitation&lt;/li&gt;
&lt;li&gt;Hands-on &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;SQL injection&lt;/a&gt;, including a live login bypass and its exact mechanism&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security&quot;&gt;PortSwigger Web Security Academy&lt;/a&gt; labs&lt;/li&gt;
&lt;li&gt;Packet analysis with &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.wireshark.org&quot;&gt;Wireshark&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview&quot;&gt;HTTP&lt;/a&gt; vs &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/HTTPS&quot;&gt;HTTPS&lt;/a&gt;, and what a &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.cloudflare.com/learning/ssl/what-happens-in-a-tls-handshake/&quot;&gt;TLS handshake&lt;/a&gt; actually changes&lt;/li&gt;
&lt;li&gt;Hands-on &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Capture_the_flag_(cybersecurity)&quot;&gt;capture-the-flag&lt;/a&gt; networking challenges&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;session-two-devsecops-and-ci-cd-pipeline-security&quot;&gt;Session Two: DevSecOps and CI/CD Pipeline Security&lt;/h3&gt;
&lt;p&gt;The second session was delivered to the Third Year B.Tech batch under the Cybersecurity Specialization track, and shifted focus from web applications to &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/CI/CD&quot;&gt;CI/CD&lt;/a&gt; pipelines, treating the build pipeline itself as a genuine &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Attack_surface&quot;&gt;attack surface&lt;/a&gt; rather than deployment plumbing that happens to exist alongside the real application.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/f6rzide1llv72eh32zaj.jpg&quot; alt=&quot;DevSecOps live demo slide showing a leaked secret in a CI/CD pipeline&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;two-buckets-six-pillars&quot;&gt;Two Buckets, Six Pillars&lt;/h4&gt;
&lt;p&gt;The session opened with a framing carried through the rest of the day: every pipeline compromise is either unauthorized tampering, someone changing what gets built or how, or unauthorized disclosure, something leaking that should not have. Six specific pillars of pipeline security were mapped across those two categories: build source, build process, build server, and build pipeline under tampering; build environment and build secrets under disclosure.&lt;/p&gt;
&lt;p&gt;Two of those pillars were covered as concept-only stories, since neither is safe to demonstrate hands-on. Dependency confusion, the build process risk, was explained through &lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610&quot;&gt;Alex Birsan's&lt;/a&gt; well-documented 2021 research, in which publishing a public package under the same name as a company's internal package caused package managers to silently prefer the attacker's version over the real one, affecting real companies including Apple, Microsoft, and PayPal. The build server risk was explained through the well-known danger of an exposed, unauthenticated &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.jenkins.io/doc/book/managing/script-console/&quot;&gt;Jenkins script console&lt;/a&gt;, which hands an attacker direct code execution on infrastructure that usually holds real deployment credentials.&lt;/p&gt;
&lt;h4 id=&quot;a-pipeline-that-looks-completely-green&quot;&gt;A Pipeline That Looks Completely Green&lt;/h4&gt;
&lt;p&gt;The live demonstration opened with a pipeline that looked entirely normal, every check passing, nothing flagged, and then walked through exactly why that signals less than it appears to. Students were shown a real, planted secret still recoverable from &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History&quot;&gt;git history&lt;/a&gt; long after the file that originally introduced it had been deleted in a later commit, since deleting a line from the working tree never deletes the commit that added it. Anyone running &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/docs/git-log&quot;&gt;&lt;code&gt;git log -p&lt;/code&gt;&lt;/a&gt; against the full history, or using GitHub's own &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/search-github/searching-on-github/searching-commits&quot;&gt;commit search&lt;/a&gt;, could recover it in seconds.&lt;/p&gt;
&lt;p&gt;Two more disclosure mechanisms were demonstrated: a secret printed in full, plain text into a &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/actions&quot;&gt;GitHub Actions&lt;/a&gt; log through a debug line, because GitHub only automatically masks values that have been explicitly registered as &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions&quot;&gt;secrets&lt;/a&gt;, never plain environment variables, no matter how sensitive their name sounds. And a secret shipped unintentionally inside a downloadable &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts&quot;&gt;build artifact&lt;/a&gt;, generated at build time and swept up by a packaging step that copied everything in an output folder without anyone deciding that on purpose.&lt;/p&gt;
&lt;p&gt;Every vulnerability was paired immediately with its fix. Automated &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/code-security/secret-scanning/introduction/about-secret-scanning&quot;&gt;secret scanning&lt;/a&gt; with a tool like &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/gitleaks/gitleaks&quot;&gt;gitleaks&lt;/a&gt;, added as a single &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/actions/using-workflows/about-workflows&quot;&gt;workflow&lt;/a&gt; step, catches both the git-history leak and the log leak at once, since it scans committed workflow content as well as commit history. &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches&quot;&gt;Branch protection&lt;/a&gt; requiring a genuine second reviewer, with the author's own approval explicitly excluded from satisfying that requirement, closes the &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Separation_of_duties&quot;&gt;segregation-of-duties&lt;/a&gt; gap that exists by default on any fresh repository or personal &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks&quot;&gt;fork&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;a-real-repository-forked-and-fixed&quot;&gt;A Real Repository, Forked and Fixed&lt;/h4&gt;
&lt;p&gt;Students then &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/about-forks&quot;&gt;forked&lt;/a&gt; a real, purpose-built vulnerable pipeline repository, containing the exact issues demonstrated live, and worked through finding and fixing each one themselves. Submissions were validated live against a shared leaderboard built entirely on &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/issues&quot;&gt;GitHub Issues&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/actions&quot;&gt;GitHub Actions&lt;/a&gt;: each &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates&quot;&gt;issue template&lt;/a&gt; mapped to one vulnerability class, an automated workflow checked the submitted flag against a stored value, and a correct answer closed the issue automatically with a live-updating rank.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Topics covered in Session Two:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The two-bucket framing: unauthorized tampering vs unauthorized disclosure&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610&quot;&gt;Dependency confusion&lt;/a&gt; and real-world &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Supply_chain_attack&quot;&gt;supply chain attacks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Exposed &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.jenkins.io/doc/book/managing/script-console/&quot;&gt;Jenkins script consoles&lt;/a&gt; and build server risk&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Separation_of_duties&quot;&gt;Segregation of duties&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches&quot;&gt;branch protection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Recovering secrets from &lt;a rel=&quot;nofollow&quot; href=&quot;https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History&quot;&gt;git history&lt;/a&gt; after deletion&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/actions&quot;&gt;GitHub Actions&lt;/a&gt; log masking, and exactly where it does not apply&lt;/li&gt;
&lt;li&gt;Secrets leaking through downloadable &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts&quot;&gt;build artifacts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Automated &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/code-security/secret-scanning/introduction/about-secret-scanning&quot;&gt;secret scanning&lt;/a&gt; with &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/gitleaks/gitleaks&quot;&gt;gitleaks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Hands-on exploitation and remediation of a live, forkable vulnerable pipeline&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;session-three-honeypots-and-intrusion-detection&quot;&gt;Session Three: Honeypots and Intrusion Detection&lt;/h3&gt;
&lt;p&gt;The final session, for Final Year B.Tech students under the Cybersecurity Specialization track, went furthest into building rather than using existing security tooling, and closed the day by tying every hour of it back to real industry practice.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/wsrecbz5cirw8lmxno28.jpg&quot; alt=&quot;Honeypot and intrusion detection session briefing presented to Final Year B.Tech students&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;what-a-honeypot-actually-is&quot;&gt;What a Honeypot Actually Is&lt;/h4&gt;
&lt;p&gt;A &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Honeypot_(computing)&quot;&gt;honeypot&lt;/a&gt; is a target that exists purely to be attacked. It has no real users and no real data, so any traffic it receives is either a mistake or an attacker, with nothing in between, which makes everything a honeypot logs suspicious by definition. The session distinguished low-interaction honeypots, which fake just enough to be convincing, from high-interaction honeypots, which run genuine software and accept real risk to capture more detail, and explained why the low-interaction approach was the deliberate choice for this exercise.&lt;/p&gt;
&lt;p&gt;Rather than installing an existing honeypot framework, students were given a small web application, built with &lt;a rel=&quot;nofollow&quot; href=&quot;https://expressjs.com&quot;&gt;Express&lt;/a&gt; and backed by an embedded SQL engine, containing three vulnerabilities: &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;SQL injection&lt;/a&gt; on the login form, weak, guessable admin credentials, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/cross-site-scripting/stored&quot;&gt;stored cross-site scripting&lt;/a&gt; through a guestbook feature that rendered submitted messages with zero &lt;a rel=&quot;nofollow&quot; href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html&quot;&gt;output escaping&lt;/a&gt;. Alongside it sat a second, hardened server: the same login flow, but with a properly &lt;a rel=&quot;nofollow&quot; href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html&quot;&gt;hashed&lt;/a&gt; password and a &lt;a rel=&quot;nofollow&quot; href=&quot;https://cheatsheetseries.owasp.org/cheatsheets/Query_Parameterization_Cheat_Sheet.html&quot;&gt;parameterized query&lt;/a&gt;, so the identical SQL injection payload that worked against the honeypot did nothing against it, a direct, visible contrast between vulnerable and secure code handling the exact same logic.&lt;/p&gt;
&lt;p&gt;Students extended the honeypot with vulnerabilities of their own choosing before a few were called up to demonstrate what they had added, with the strongest additions merged into one shared honeypot that, by the end of the session, had genuinely been built collectively by the room.&lt;/p&gt;
&lt;h4 id=&quot;from-signature-detection-to-a-real-threshold-rule&quot;&gt;From Signature Detection to a Real Threshold Rule&lt;/h4&gt;
&lt;p&gt;The second half of the session covered &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Intrusion_detection_system&quot;&gt;intrusion detection&lt;/a&gt; from first principles: signature-based detection, matching traffic against a library of known-bad patterns; anomaly-based detection, flagging deviation from a learned baseline of normal behavior; and threshold-based detection, simply counting events and firing once a number is crossed within a time window. Students were shown that a genuinely large share of real production alerting is built on the third, simplest category, not the more sophisticated-sounding first two.&lt;/p&gt;
&lt;p&gt;Every request against the honeypot was tagged with an anonymous session identifier, assigned on first contact through a &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies&quot;&gt;cookie&lt;/a&gt; rather than relying on &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/IP_address&quot;&gt;IP address&lt;/a&gt;, since every attacker in the same room shares an IP on a shared network. Students then examined a hand-written, &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript&quot;&gt;JavaScript&lt;/a&gt;-based intrusion detection script watching the honeypot's activity log in real time, implementing a single rule: five or more login attempts from the same session within ten seconds triggers an alert.&lt;/p&gt;
&lt;p&gt;A live, fast attack against the honeypot triggered that alert within seconds of the fifth attempt. A second attack, run against the exact same target with the exact same wordlist, was then paced deliberately at three-second intervals instead, and slipped entirely underneath the same threshold, twelve attempts, zero alerts, an honest and real limitation of threshold-based detection rather than a flaw in the exercise, and exactly the gap that real attackers who understand detection systems deliberately exploit.&lt;/p&gt;
&lt;h4 id=&quot;mapping-the-day-back-to-the-real-industry&quot;&gt;Mapping the Day Back to the Real Industry&lt;/h4&gt;
&lt;p&gt;The session closed by naming the real tools every piece of the day's exercise mirrors at production scale: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/cowrie/cowrie&quot;&gt;Cowrie&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/telekom-security/tpotce&quot;&gt;T-Pot&lt;/a&gt; as real honeypot platforms used in &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Security_operations_center&quot;&gt;SOC&lt;/a&gt; training and research, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.greynoise.io&quot;&gt;GreyNoise&lt;/a&gt; as a commercial company built entirely on turning honeypot telemetry into a &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Cyber_threat_intelligence&quot;&gt;threat intelligence&lt;/a&gt; product, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.snort.org&quot;&gt;Snort&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://suricata.io&quot;&gt;Suricata&lt;/a&gt;, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://zeek.org&quot;&gt;Zeek&lt;/a&gt; as real network intrusion detection engines, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://wazuh.com&quot;&gt;Wazuh&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/fail2ban/fail2ban&quot;&gt;fail2ban&lt;/a&gt; as the closest real analogues to the hand-written detection script, both fundamentally watching a log and counting events past a threshold. &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/SigmaHQ/sigma&quot;&gt;Sigma&lt;/a&gt;, a platform-agnostic format for writing detection logic once and translating it into whatever query language a given &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Security_information_and_event_management&quot;&gt;SIEM&lt;/a&gt; platform speaks, was shown directly alongside the session's own JavaScript threshold rule to make the mapping concrete rather than asserted. The session's &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Brute-force_attack&quot;&gt;brute-force&lt;/a&gt; detection was tied to &lt;a rel=&quot;nofollow&quot; href=&quot;https://attack.mitre.org&quot;&gt;MITRE ATT&amp;amp;CK&lt;/a&gt; technique &lt;a rel=&quot;nofollow&quot; href=&quot;https://attack.mitre.org/techniques/T1110/&quot;&gt;T1110&lt;/a&gt;, the shared, precise vocabulary security teams worldwide use to describe this exact behavior, and the day closed by naming the career paths this kind of work leads into: SOC Analyst, Detection Engineer, and Threat Hunter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Topics covered in Session Three:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What a &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Honeypot_(computing)&quot;&gt;honeypot&lt;/a&gt; is, and low interaction vs high interaction design&lt;/li&gt;
&lt;li&gt;Building a web &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Honeypot_(computing)&quot;&gt;honeypot&lt;/a&gt; with &lt;a rel=&quot;nofollow&quot; href=&quot;https://expressjs.com&quot;&gt;Express&lt;/a&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;SQL injection&lt;/a&gt;, weak credentials, &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/cross-site-scripting/stored&quot;&gt;stored XSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A hardened comparison server, and exactly what makes it hardened&lt;/li&gt;
&lt;li&gt;Anonymous session tracking and full activity logging&lt;/li&gt;
&lt;li&gt;Signature-based, anomaly-based, and threshold-based &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Intrusion_detection_system&quot;&gt;intrusion detection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Writing a &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript&quot;&gt;JavaScript&lt;/a&gt;-based threshold detection script from scratch&lt;/li&gt;
&lt;li&gt;Simulating live attacks and triggering real-time alerts&lt;/li&gt;
&lt;li&gt;Testing slow-rate attacks designed to evade threshold detection&lt;/li&gt;
&lt;li&gt;Real industry tools: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/cowrie/cowrie&quot;&gt;Cowrie&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/telekom-security/tpotce&quot;&gt;T-Pot&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.greynoise.io&quot;&gt;GreyNoise&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.snort.org&quot;&gt;Snort&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://suricata.io&quot;&gt;Suricata&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://zeek.org&quot;&gt;Zeek&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://wazuh.com&quot;&gt;Wazuh&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/fail2ban/fail2ban&quot;&gt;fail2ban&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/SigmaHQ/sigma&quot;&gt;Sigma&lt;/a&gt; detection rules and the &lt;a rel=&quot;nofollow&quot; href=&quot;https://attack.mitre.org&quot;&gt;MITRE ATT&amp;amp;CK&lt;/a&gt; framework&lt;/li&gt;
&lt;li&gt;Career paths: SOC Analyst, Detection Engineer, Threat Hunter&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;frequently-asked-questions&quot;&gt;Frequently Asked Questions&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;What is a honeypot in cybersecurity?&lt;/strong&gt;&lt;br&gt;
A &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Honeypot_(computing)&quot;&gt;honeypot&lt;/a&gt; is a decoy system with no real users or data, deployed specifically to attract and record attacker behavior. Because it serves no legitimate purpose, any traffic it receives is inherently suspicious, making it a useful, low-noise source of real attack data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is DevSecOps?&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/DevSecOps&quot;&gt;DevSecOps&lt;/a&gt; is the practice of integrating security checks directly into the software development and deployment pipeline, rather than treating security as a separate, later stage. It covers securing the source code, the build process, the build server, the pipeline configuration itself, the deployment environment, and any secrets or credentials the pipeline handles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is SQL injection?&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security/sql-injection&quot;&gt;SQL injection&lt;/a&gt; is a vulnerability where user-supplied input is inserted directly into a database query without being properly separated from the query's actual logic, allowing an attacker to alter what the query does, commonly used to bypass login forms or extract data the application was never meant to expose.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is an intrusion detection system (IDS)?&lt;/strong&gt;&lt;br&gt;
An &lt;a rel=&quot;nofollow&quot; href=&quot;https://en.wikipedia.org/wiki/Intrusion_detection_system&quot;&gt;intrusion detection system&lt;/a&gt; monitors activity, whether network traffic or system logs, for signs of malicious behavior, using signature matching, anomaly detection, or threshold-based rules, and raises an alert when it finds something worth a human's attention.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is the difference between HTTP and HTTPS?&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/HTTPS&quot;&gt;HTTPS&lt;/a&gt; is HTTP with an additional encryption layer, &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/Security/Transport_Layer_Security&quot;&gt;TLS&lt;/a&gt;, negotiated before any application data is sent. HTTP traffic travels in plain, readable text; HTTPS traffic is encrypted end to end between browser and server, though it does not protect against something running locally on the same machine, such as a trusted &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling&quot;&gt;proxy&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;hands-on-cybersecurity-training-start-to-finish&quot;&gt;Hands-On Cybersecurity Training, Start to Finish&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/5xxcpmq4t9m5s6axvkyt.jpg&quot; alt=&quot;Students collaborating during a hands-on lab at the ITM Skills University cybersecurity workshop&quot;&gt;&lt;/p&gt;
&lt;p&gt;Across all three sessions, the format stayed consistent: an exploit demonstrated live first, the underlying concept explained second, and a genuine hands-on lab third, whether that meant a &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/web-security&quot;&gt;PortSwigger Web Security Academy&lt;/a&gt; lab, a forkable vulnerable GitHub repository, or a self-built honeypot and detection script. Nothing in any of the three sessions ran as a simulation standing in for the real thing, and every piece of infrastructure used across the day, from the &lt;a rel=&quot;nofollow&quot; href=&quot;https://portswigger.net/burp&quot;&gt;Burp Suite&lt;/a&gt; setup to the vulnerable pipeline repository to the honeypot's embedded database, was tested end to end in advance.&lt;/p&gt;
&lt;h3 id=&quot;thank-you&quot;&gt;Thank You&lt;/h3&gt;
&lt;p&gt;A huge thank you to &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ayaanshaikhhh&quot;&gt;Ayaan&lt;/a&gt;, who was there through all three sessions, helping keep everything running smoothly from setup to wrap. Thank you as well to ITM Skills University and the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.itm.edu/school-of-futuretech/&quot;&gt;School of Future Tech&lt;/a&gt; for hosting the day, and to every student across all three batches, Second Year, Third Year, and Final Year B.Tech, who showed up, asked sharp questions, and actually broke things.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/g8hwpa3u02kmxb9ytdo7.jpg&quot; alt=&quot;Group photo of students and organizers at the end of the cybersecurity workshop at ITM Skills University, Kharghar&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;about-arham-sayyed&quot;&gt;About Arham Sayyed&lt;/h3&gt;
&lt;p&gt;Arham Sayyed is a Mumbai-based full-stack developer, security researcher, and cybersecurity educator, working across &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.mongodb.com/mern-stack&quot;&gt;MERN&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.typescriptlang.org&quot;&gt;TypeScript&lt;/a&gt;, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://nextjs.org&quot;&gt;Next.js&lt;/a&gt; in both product development and freelance client work. He lectures on cybersecurity and related subjects for BSc CA/BCA students under the Mumbai University NEP syllabus, and builds practical cybersecurity training infrastructure, including the vulnerable pipeline repository and the honeypot and intrusion detection setup used in this workshop. He is a Platform Engineering Volunteer at &lt;a rel=&quot;nofollow&quot; href=&quot;https://haxnation.org/&quot;&gt;Haxnation&lt;/a&gt;, a cybersecurity community running a &lt;a rel=&quot;nofollow&quot; href=&quot;https://ctf.haxnation.org/&quot;&gt;CTF platform&lt;/a&gt;, meetups, and workshops like this one.&lt;/p&gt;
&lt;p&gt;Connect with Arham Sayyed on &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/arham-sayyed&quot;&gt;GitHub&lt;/a&gt; or &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/arhamsayyed_dev&quot;&gt;X&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29148/arham-sayyeds-cybersecurity-workshop-skills-university-security-devsecops-honeypots</guid>
<pubDate>Tue, 29 Sep 2026 20:13:45 +0000</pubDate>
</item>
<item>
<title>Going Back to TypeScript Basics</title>
<link>https://coderlegion.com/28702/going-back-to-typescript-basics</link>
<description>&lt;p&gt;After finishing my JavaScript refresher, TypeScript was next.&lt;/p&gt;
&lt;p&gt;I’ve used TypeScript before, so I went into this thinking it would mostly be a case of reminding myself of things I already knew.&lt;/p&gt;
&lt;p&gt;That was partly true.&lt;/p&gt;
&lt;p&gt;The basic stuff came back pretty quickly. Types on variables, functions, arrays, objects — nothing too surprising there.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const memberCount: number = 10;

function isAdmin(role: string): boolean {
    return role === &quot;admin&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But as I went further, I started noticing the same thing that happened during my JavaScript refresher.&lt;/p&gt;
&lt;p&gt;There’s a difference between recognising something and actually understanding it.&lt;/p&gt;
&lt;p&gt;I could look at a generic and think, &lt;em&gt;yeah, I know what that is&lt;/em&gt;. But could I write one myself? Could I explain why I needed it? Could I tell when I &lt;strong&gt;didn't&lt;/strong&gt; need one?&lt;/p&gt;
&lt;p&gt;Not always.&lt;/p&gt;
&lt;p&gt;And that’s really why I wanted to do this refresher in the first place.&lt;/p&gt;
&lt;h3 id=&quot;typescript-started-making-more-sense-when-i-stopped-fighting-it&quot;&gt;TypeScript started making more sense when I stopped fighting it&lt;/h3&gt;
&lt;p&gt;One thing that became clearer this time was type narrowing.&lt;/p&gt;
&lt;p&gt;Say I have:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;interface Member {
    name: string;
    profilePicture: string | null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I can't just do:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;member.profilePicture.toUpperCase();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;because TypeScript doesn't know whether &lt;code&gt;profilePicture&lt;/code&gt; is actually a string.&lt;/p&gt;
&lt;p&gt;My first instinct in the past would've probably been something along the lines of, &lt;em&gt;I know it's a string here, why are you complaining?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;But that's the point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I&lt;/strong&gt; might know what I expect the value to be. TypeScript wants me to prove it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (member.profilePicture !== null) {
    member.profilePicture.toUpperCase();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once I started looking at narrowing like that, it stopped feeling like TypeScript getting in my way.&lt;/p&gt;
&lt;p&gt;The same thing happened with &lt;code&gt;.find()&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function findMember(
    members: Member[],
    name: string
): Member | undefined {
    return members.find((member) =&amp;gt; member.name === name);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I might expect the member to exist, but &lt;code&gt;.find()&lt;/code&gt; doesn't care about my expectations. It can return &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So the type should say that.&lt;/p&gt;
&lt;p&gt;Simple, but important.&lt;/p&gt;
&lt;h3 id=&quot;generics-weren-t-as-scary-as-they-looked&quot;&gt;Generics weren't as scary as they looked&lt;/h3&gt;
&lt;p&gt;Generics were another one.&lt;/p&gt;
&lt;p&gt;This:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function getLast&amp;lt;T&amp;gt;(items: T[]): T | undefined {
    return items[items.length - 1];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;used to look more complicated than it actually is.&lt;/p&gt;
&lt;p&gt;Once I started thinking of &lt;code&gt;T&lt;/code&gt; as basically saying &lt;em&gt;“whatever type you give me”&lt;/em&gt;, it became much easier to read.&lt;/p&gt;
&lt;p&gt;Give it strings?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;T&lt;/code&gt; is a string.&lt;/p&gt;
&lt;p&gt;Give it numbers?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;T&lt;/code&gt; is a number.&lt;/p&gt;
&lt;p&gt;Give it members?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;T&lt;/code&gt; is a &lt;code&gt;Member&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then we got to things like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function getPostValue&amp;lt;K extends keyof Post&amp;gt;(
    post: Post,
    key: K
): Post[K] {
    return post[key];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, I'll admit, something like this still looks a bit mad when you first see it.&lt;/p&gt;
&lt;p&gt;But breaking it apart helped.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;keyof Post&lt;/code&gt; gives me the valid keys from &lt;code&gt;Post&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;K&lt;/code&gt; is one of those keys.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Post[K]&lt;/code&gt; is the type of whatever property I asked for.&lt;/p&gt;
&lt;p&gt;So if &lt;code&gt;&quot;title&quot;&lt;/code&gt; points to a string, TypeScript knows I'm getting a string back. If &lt;code&gt;&quot;likes&quot;&lt;/code&gt; points to a number, I get a number.&lt;/p&gt;
&lt;p&gt;It wasn't really about memorising the syntax. It was understanding what each part was trying to describe.&lt;/p&gt;
&lt;h3 id=&quot;i-managed-to-confuse-pick-and-omit&quot;&gt;I managed to confuse Pick and Omit&lt;/h3&gt;
&lt;p&gt;We also went through TypeScript's utility types: &lt;code&gt;Partial&lt;/code&gt;, &lt;code&gt;Pick&lt;/code&gt;, &lt;code&gt;Omit&lt;/code&gt; and &lt;code&gt;Record&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Most of it was straightforward.&lt;/p&gt;
&lt;p&gt;Then I got asked something like:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If you only want &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;company&lt;/code&gt; and &lt;code&gt;salary&lt;/code&gt; from a &lt;code&gt;Job&lt;/code&gt; type, which utility type would you use?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I said &lt;code&gt;Omit&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My reasoning?&lt;/p&gt;
&lt;p&gt;Because I only wanted &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;company&lt;/code&gt; and &lt;code&gt;salary&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Which is literally the reason I should have used &lt;code&gt;Pick&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;😂&lt;/p&gt;
&lt;p&gt;That mistake actually made the difference easier to remember than just reading the documentation would have.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pick what you want to keep.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Omit what you want to remove.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Sometimes getting something wrong once is all it takes.&lt;/p&gt;
&lt;h3 id=&quot;then-things-started-connecting&quot;&gt;Then things started connecting&lt;/h3&gt;
&lt;p&gt;Discriminated unions were where TypeScript started feeling less like “adding types to JavaScript” and more like actually modelling how an application works.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;type MemberState =
    | { status: &quot;loading&quot; }
    | { status: &quot;success&quot;; members: Member[] }
    | { status: &quot;error&quot;; message: string };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the status is &lt;code&gt;&quot;success&quot;&lt;/code&gt;, TypeScript knows there are members.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (state.status === &quot;success&quot;) {
    console.log(state.members);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If it's &lt;code&gt;&quot;error&quot;&lt;/code&gt;, TypeScript knows there's a message.&lt;/p&gt;
&lt;p&gt;And if it's &lt;code&gt;&quot;loading&quot;&lt;/code&gt;, neither of those things should exist yet.&lt;/p&gt;
&lt;p&gt;That makes sense because that's how the application actually behaves.&lt;/p&gt;
&lt;p&gt;We also looked at &lt;code&gt;never&lt;/code&gt; for checking that every possible state has been handled.&lt;/p&gt;
&lt;p&gt;That was when I started seeing that a lot of these TypeScript features aren't really separate things.&lt;/p&gt;
&lt;p&gt;They build on each other.&lt;/p&gt;
&lt;p&gt;Union types lead to narrowing.&lt;/p&gt;
&lt;p&gt;Narrowing makes discriminated unions useful.&lt;/p&gt;
&lt;p&gt;Discriminated unions make exhaustive checking possible.&lt;/p&gt;
&lt;p&gt;The individual syntax started mattering less than the relationship between everything.&lt;/p&gt;
&lt;h3 id=&quot;type-predicates-were-another-good-example&quot;&gt;Type predicates were another good example&lt;/h3&gt;
&lt;p&gt;We wrote things like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function isAdmin(user: User): user is Admin {
    return user.role === &quot;admin&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The interesting bit for me was:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;user is Admin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We're not just returning &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We're telling TypeScript:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If this function returns true, I've checked this value and you can treat it as an Admin.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That's powerful, but there's also a catch.&lt;/p&gt;
&lt;p&gt;TypeScript trusts you.&lt;/p&gt;
&lt;p&gt;If the function says &lt;code&gt;user is Admin&lt;/code&gt; but my actual logic checks for a member instead, TypeScript isn't going to magically save me.&lt;/p&gt;
&lt;p&gt;I'd basically be lying to the compiler.&lt;/p&gt;
&lt;p&gt;That distinction between what TypeScript can check and what &lt;strong&gt;my code still needs to get right&lt;/strong&gt; came up again later.&lt;/p&gt;
&lt;h3 id=&quot;api-data-made-unknown-click-for-me&quot;&gt;API data made &lt;code&gt;unknown&lt;/code&gt; click for me&lt;/h3&gt;
&lt;p&gt;This was probably one of the most useful parts of the refresher.&lt;/p&gt;
&lt;p&gt;Imagine I do this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const data: Member[] = await response.json();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looks nice.&lt;/p&gt;
&lt;p&gt;My editor is happy.&lt;/p&gt;
&lt;p&gt;TypeScript thinks I have an array of members.&lt;/p&gt;
&lt;p&gt;But what if the API sends something completely different?&lt;/p&gt;
&lt;p&gt;Writing &lt;code&gt;Member[]&lt;/code&gt; doesn't magically transform the response into valid member data.&lt;/p&gt;
&lt;p&gt;I'm basically just telling TypeScript to trust me.&lt;/p&gt;
&lt;p&gt;A safer starting point is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const data: unknown = await response.json();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now I have to prove what the data actually is before using it.&lt;/p&gt;
&lt;p&gt;We ended up building a validator manually:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function isMember(value: unknown): value is Member {
    if (typeof value !== &quot;object&quot; || value === null) {
        return false;
    }

    if (!(&quot;name&quot; in value) || typeof value.name !== &quot;string&quot;) {
        return false;
    }

    if (!(&quot;id&quot; in value) || typeof value.id !== &quot;number&quot;) {
        return false;
    }

    if (
        !(&quot;role&quot; in value) ||
        (value.role !== &quot;admin&quot; &amp;amp;&amp;amp; value.role !== &quot;member&quot;)
    ) {
        return false;
    }

    if (
        !(&quot;isOnline&quot; in value) ||
        typeof value.isOnline !== &quot;boolean&quot;
    ) {
        return false;
    }

    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And, of course, I got some of the logic wrong at first.&lt;/p&gt;
&lt;p&gt;I was doing something similar to:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if ((&quot;id&quot; in value) &amp;amp;&amp;amp; typeof value.id === &quot;number&quot;) {
    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my head I was checking that the &lt;code&gt;id&lt;/code&gt; was valid.&lt;/p&gt;
&lt;p&gt;Which I was.&lt;/p&gt;
&lt;p&gt;The problem was that I was then returning &lt;code&gt;true&lt;/code&gt; for the &lt;strong&gt;entire object&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;So apparently having a valid ID was enough to become a valid member. Name? Role? Online status?&lt;/p&gt;
&lt;p&gt;Who needs those? 😂&lt;/p&gt;
&lt;p&gt;The better way was:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (!(&quot;id&quot; in value) || typeof value.id !== &quot;number&quot;) {
    return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check each reason the object could be invalid, reject it when something is wrong, and only return &lt;code&gt;true&lt;/code&gt; once it survives all the checks.&lt;/p&gt;
&lt;p&gt;That wasn't even really a TypeScript problem.&lt;/p&gt;
&lt;p&gt;It was a logic problem.&lt;/p&gt;
&lt;p&gt;And that's exactly the kind of thing I wanted this refresher to expose.&lt;/p&gt;
&lt;h3 id=&quot;by-the-end-the-types-were-doing-something-useful&quot;&gt;By the end, the types were doing something useful&lt;/h3&gt;
&lt;p&gt;Towards the end, I had to model members and admins.&lt;/p&gt;
&lt;p&gt;My first attempt was:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;type Role = &quot;admin&quot; | &quot;member&quot;;

interface Member {
    id: number;
    name: string;
    role: Role;
    isOnline: boolean;
}

interface Admin extends Member {
    permissions: string[];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Looks reasonable.&lt;/p&gt;
&lt;p&gt;Except there's a problem.&lt;/p&gt;
&lt;p&gt;Because &lt;code&gt;Admin&lt;/code&gt; inherits &lt;code&gt;role: Role&lt;/code&gt;, this is technically possible:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const admin: Admin = {
    id: 1,
    name: &quot;Sabi&quot;,
    role: &quot;member&quot;,
    isOnline: true,
    permissions: [&quot;delete-post&quot;]
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An admin whose role is &lt;code&gt;&quot;member&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Not exactly ideal.&lt;/p&gt;
&lt;p&gt;So we changed the model:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;interface BaseUser {
    id: number;
    name: string;
    isOnline: boolean;
}

interface Member extends BaseUser {
    role: &quot;member&quot;;
}

interface Admin extends BaseUser {
    role: &quot;admin&quot;;
    permissions: string[];
}

type User = Member | Admin;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now an &lt;code&gt;Admin&lt;/code&gt; has to be an admin.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Member&lt;/code&gt; has to be a member.&lt;/p&gt;
&lt;p&gt;Then I could write:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function getOnlineAdmins(users: User[]): Admin[] {
    return users.filter(
        (user) =&amp;gt; user.role === &quot;admin&quot; &amp;amp;&amp;amp; user.isOnline
    );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's a small example, but I think it sums up what I've taken from this refresher.&lt;/p&gt;
&lt;p&gt;Before, I probably would have looked at TypeScript mainly as a way of saying:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;name: string
age: number
isOnline: boolean
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's part of it.&lt;/p&gt;
&lt;p&gt;But good types can describe much more than that.&lt;/p&gt;
&lt;p&gt;They can describe what states are possible, what data might be missing, what kind of user you're dealing with, what a function actually returns and what your application should and shouldn't allow.&lt;/p&gt;
&lt;h3 id=&quot;so-do-i-know-typescript-now-&quot;&gt;So, do I know TypeScript now?&lt;/h3&gt;
&lt;p&gt;More than I did before.&lt;/p&gt;
&lt;p&gt;Everything? Definitely not.&lt;/p&gt;
&lt;p&gt;There are still advanced parts of TypeScript that I haven't touched, and I'm sure I'll run into plenty of things that make me stare at my screen wondering what on earth I'm looking at.&lt;/p&gt;
&lt;p&gt;But I don't think I need to know all of TypeScript before moving forward.&lt;/p&gt;
&lt;p&gt;That wasn't the point.&lt;/p&gt;
&lt;p&gt;The point was to get comfortable enough with the fundamentals that when I start using TypeScript properly with React, I'm not just adding types until the red lines disappear.&lt;/p&gt;
&lt;p&gt;More importantly, I'm starting to understand what TypeScript is trying to tell me when those red lines appear.&lt;/p&gt;
&lt;p&gt;JavaScript refresher done.&lt;/p&gt;
&lt;p&gt;TypeScript refresher done.&lt;/p&gt;
&lt;p&gt;Now, React.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28702/going-back-to-typescript-basics</guid>
<pubDate>Sat, 26 Sep 2026 09:17:06 +0000</pubDate>
</item>
<item>
<title>Personal Expense Tracker V2 — Edit, Validation &amp; Error Handling</title>
<link>https://coderlegion.com/28205/personal-expense-tracker-v2-edit-validation-error-handling</link>
<description>&lt;p&gt;I’ve completed &lt;strong&gt;V2&lt;/strong&gt; of my Personal Expense Tracker &lt;/p&gt;
&lt;p&gt;This project started as a way to practice &lt;strong&gt;Python OOP, SQL, and SQLite&lt;/strong&gt; by building something practical instead of only studying theory.&lt;/p&gt;
&lt;p&gt;V1 focused on getting the core CRUD operations working.&lt;/p&gt;
&lt;p&gt;With V2, I focused on making the application more reliable and user-friendly.&lt;/p&gt;
&lt;h3 id=&quot;what-i-added-in-v2&quot;&gt;What I Added in V2&lt;/h3&gt;
&lt;h4 id=&quot;-edit-expenses&quot;&gt;✏️ Edit Expenses&lt;/h4&gt;
&lt;p&gt;Users can now modify an existing expense.&lt;/p&gt;
&lt;p&gt;The edit flow allows changing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Amount&lt;/li&gt;
&lt;li&gt;Category&lt;/li&gt;
&lt;li&gt;Description&lt;/li&gt;
&lt;li&gt;Date&lt;/li&gt;
&lt;li&gt;Cancel the operation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This also gave me more practical experience working with SQL &lt;code&gt;UPDATE&lt;/code&gt; statements and designing application flow around user choices.&lt;/p&gt;
&lt;h4 id=&quot;-input-validation&quot;&gt;️ Input Validation&lt;/h4&gt;
&lt;p&gt;I also added validation for user input.&lt;/p&gt;
&lt;p&gt;The application now handles things such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Invalid menu choices&lt;/li&gt;
&lt;li&gt;Invalid amount input&lt;/li&gt;
&lt;li&gt;Negative or zero amounts&lt;/li&gt;
&lt;li&gt;Invalid category selection&lt;/li&gt;
&lt;li&gt;Empty descriptions&lt;/li&gt;
&lt;li&gt;Invalid dates&lt;/li&gt;
&lt;li&gt;Impossible dates such as &lt;code&gt;2026-02-30&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, instead of allowing invalid input to crash the program, the application asks the user to provide a valid value.&lt;/p&gt;
&lt;h4 id=&quot;-error-handling&quot;&gt;⚠️ Error Handling&lt;/h4&gt;
&lt;p&gt;While implementing validation, I also started thinking more carefully about where errors can happen and how the application should respond.&lt;/p&gt;
&lt;p&gt;One important lesson was that &lt;strong&gt;input validation is not just about preventing crashes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It is about defining what the application considers valid input and controlling the flow when the user provides something unexpected.&lt;/p&gt;
&lt;h3 id=&quot;what-i-learned&quot;&gt;What I Learned&lt;/h3&gt;
&lt;p&gt;V2 taught me more than just how to add an edit feature.&lt;/p&gt;
&lt;p&gt;I learned about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Designing validation rules before implementing them&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;try/except&lt;/code&gt; for user input&lt;/li&gt;
&lt;li&gt;Separating application flow from business logic&lt;/li&gt;
&lt;li&gt;Handling different types of invalid input&lt;/li&gt;
&lt;li&gt;Thinking about edge cases&lt;/li&gt;
&lt;li&gt;Designing better CLI user interaction&lt;/li&gt;
&lt;li&gt;Keeping the project scope under control&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One thing I learned during this version is that &lt;strong&gt;not every possible improvement needs to be implemented immediately&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There are still things I could add, such as more advanced database error handling and additional checks, but I decided to stop V2 at the scope I originally defined.&lt;/p&gt;
&lt;p&gt;That is also part of learning software development: knowing when a version is complete.&lt;/p&gt;
&lt;h3 id=&quot;-current-architecture&quot;&gt;️ Current Architecture&lt;/h3&gt;
&lt;p&gt;The project currently follows a simple separation of responsibilities:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;main.py
   ↓
ExpenseManager
   ↓
Database / storage.py
   ↓
SQLite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;main.py&lt;/code&gt; handles the CLI/application flow, &lt;code&gt;ExpenseManager&lt;/code&gt; handles the application logic, and &lt;code&gt;storage.py&lt;/code&gt; handles database operations.&lt;/p&gt;
&lt;h3 id=&quot;v2-status&quot;&gt;V2 Status&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;V2 is now complete.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The project currently supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ Add expenses&lt;/li&gt;
&lt;li&gt;✅ View expenses&lt;/li&gt;
&lt;li&gt;✅ Edit expenses&lt;/li&gt;
&lt;li&gt;✅ Delete expenses&lt;/li&gt;
&lt;li&gt;✅ Category selection&lt;/li&gt;
&lt;li&gt;✅ Input validation&lt;/li&gt;
&lt;li&gt;✅ Date validation&lt;/li&gt;
&lt;li&gt;✅ Basic error handling&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main goal of this version was not to build a huge application.&lt;/p&gt;
&lt;p&gt;It was to take the working V1 and make it &lt;strong&gt;more reliable while strengthening my understanding of Python, OOP, SQL, SQLite, and application design.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;project&quot;&gt;Project&lt;/h3&gt;
&lt;p&gt; &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/xDK0d3r/personal-expense-tracker&quot;&gt;Personal Expense Tracker&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I’ll continue improving the project in future versions when there is something meaningful to learn or add.&lt;/p&gt;
&lt;p&gt;Thanks for reading! &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28205/personal-expense-tracker-v2-edit-validation-error-handling</guid>
<pubDate>Tue, 22 Sep 2026 17:19:39 +0000</pubDate>
</item>
<item>
<title>Personal Expense Tracker — V1 Complete: Building the Core Working Stage</title>
<link>https://coderlegion.com/28203/personal-expense-tracker-v1-complete-building-the-core-working-stage</link>
<description>&lt;p&gt;I’ve completed &lt;strong&gt;V1 of my Personal Expense Tracker&lt;/strong&gt;, a small &lt;strong&gt;learning project&lt;/strong&gt; built with &lt;strong&gt;Python and SQLite&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This project was created primarily to learn by building rather than following a tutorial step by step.&lt;/p&gt;
&lt;p&gt;The goal of V1 was not to build a production-ready expense tracker.&lt;/p&gt;
&lt;p&gt;The goal was:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Build the core application flow, understand the responsibilities of each layer, and make the fundamental functionality work end-to-end.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;With that goal achieved, I consider &lt;strong&gt;V1 complete&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;why-i-built-this-project&quot;&gt;Why I Built This Project&lt;/h3&gt;
&lt;p&gt;I wanted to strengthen my understanding of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python OOP&lt;/li&gt;
&lt;li&gt;Classes and objects&lt;/li&gt;
&lt;li&gt;Object responsibilities&lt;/li&gt;
&lt;li&gt;SQL and SQLite&lt;/li&gt;
&lt;li&gt;CRUD operations&lt;/li&gt;
&lt;li&gt;Database design&lt;/li&gt;
&lt;li&gt;Separating application responsibilities&lt;/li&gt;
&lt;li&gt;How different layers communicate with each other&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of learning these concepts separately, I decided to combine them into one small practical project.&lt;/p&gt;
&lt;p&gt;That's why I consider this primarily a &lt;strong&gt;learning project&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The objective was not to create a feature-rich application immediately, but to understand the fundamentals by actually building something.&lt;/p&gt;
&lt;h3 id=&quot;what-i-built&quot;&gt;What I Built&lt;/h3&gt;
&lt;p&gt;The application is a command-line Personal Expense Tracker.&lt;/p&gt;
&lt;p&gt;The core V1 functionality includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adding an expense&lt;/li&gt;
&lt;li&gt;Viewing expenses&lt;/li&gt;
&lt;li&gt;Deleting an expense&lt;/li&gt;
&lt;li&gt;Storing expenses in SQLite&lt;/li&gt;
&lt;li&gt;Using SQLite-generated IDs&lt;/li&gt;
&lt;li&gt;Separating the application into different responsibilities&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The database currently contains an &lt;code&gt;expenses&lt;/code&gt; table:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Column        &lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;id&lt;/code&gt;          &lt;/td&gt;&lt;td&gt;INTEGER PRIMARY KEY&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;amount&lt;/code&gt;      &lt;/td&gt;&lt;td&gt;REAL&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;category&lt;/code&gt;    &lt;/td&gt;&lt;td&gt;TEXT&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;description&lt;/code&gt; &lt;/td&gt;&lt;td&gt;TEXT&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;date&lt;/code&gt;        &lt;/td&gt;&lt;td&gt;TEXT&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;application-architecture&quot;&gt;Application Architecture&lt;/h3&gt;
&lt;p&gt;One of the main things I wanted to learn was how to organize responsibilities in an application.&lt;/p&gt;
&lt;p&gt;I structured the project into three main layers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;main.py
   ↓
ExpenseManager
   ↓
Database
   ↓
SQLite
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;main-py-ui-layer&quot;&gt;&lt;code&gt;main.py&lt;/code&gt; — UI Layer&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;main.py&lt;/code&gt; is responsible for interacting with the user through the CLI.&lt;/p&gt;
&lt;p&gt;Its responsibilities include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Displaying the menu&lt;/li&gt;
&lt;li&gt;Taking user input&lt;/li&gt;
&lt;li&gt;Calling the appropriate operation&lt;/li&gt;
&lt;li&gt;Displaying results&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The UI does not directly handle SQL operations.&lt;/p&gt;
&lt;h4 id=&quot;expense-manager-py-application-layer&quot;&gt;&lt;code&gt;expense_manager.py&lt;/code&gt; — Application Layer&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;ExpenseManager&lt;/code&gt; acts as the middle layer between the UI and storage.&lt;/p&gt;
&lt;p&gt;It coordinates application operations and communicates with the database object.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;main.py
   ↓
ExpenseManager.add_expense()
   ↓
Database.insert_expense()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This helped me understand the difference between the application's operation and the actual persistence of data.&lt;/p&gt;
&lt;h4 id=&quot;storage-py-persistence-layer&quot;&gt;&lt;code&gt;storage.py&lt;/code&gt; — Persistence Layer&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;Database&lt;/code&gt; class handles communication with SQLite.&lt;/p&gt;
&lt;p&gt;Its responsibilities include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creating the database connection&lt;/li&gt;
&lt;li&gt;Initializing the database&lt;/li&gt;
&lt;li&gt;Creating the table&lt;/li&gt;
&lt;li&gt;Inserting expenses&lt;/li&gt;
&lt;li&gt;Retrieving expenses&lt;/li&gt;
&lt;li&gt;Deleting expenses&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This keeps database-related responsibilities separate from the UI.&lt;/p&gt;
&lt;h3 id=&quot;understanding-object-relationships&quot;&gt;Understanding Object Relationships&lt;/h3&gt;
&lt;p&gt;Another important part of this project was understanding how objects can work together.&lt;/p&gt;
&lt;p&gt;The application creates a database object and passes it to &lt;code&gt;ExpenseManager&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Conceptually:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;main.py
   │
   ├── creates Database
   │
   └── creates ExpenseManager
             │
             └── uses Database
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The manager receives the database dependency instead of creating its own database internally.&lt;/p&gt;
&lt;p&gt;This was one of the OOP concepts I wanted to understand through practical implementation.&lt;/p&gt;
&lt;h3 id=&quot;why-v1-is-complete&quot;&gt;Why V1 Is Complete&lt;/h3&gt;
&lt;p&gt;There is an important distinction between:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;V1 is complete&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;the project is finished.&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;V1 is complete because its intended scope has been achieved.&lt;/p&gt;
&lt;p&gt;The core application flow works:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;User
 ↓
CLI
 ↓
ExpenseManager
 ↓
Database
 ↓
SQLite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An expense can be added and stored, stored expenses can be retrieved and displayed, and expenses can be deleted.&lt;/p&gt;
&lt;p&gt;The different layers can communicate with each other correctly.&lt;/p&gt;
&lt;p&gt;That is the foundation I wanted to establish in V1.&lt;/p&gt;
&lt;h3 id=&quot;what-i-learned-from-v1&quot;&gt;What I Learned From V1&lt;/h3&gt;
&lt;p&gt;The biggest lesson wasn't simply learning SQLite syntax or Python syntax.&lt;/p&gt;
&lt;p&gt;It was understanding &lt;strong&gt;responsibility&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I learned to think about the application as separate parts with different jobs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;UI
→ communicates with the user

Manager
→ coordinates application operations

Storage
→ communicates with the database
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Along the way, I gained practical experience with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python classes&lt;/li&gt;
&lt;li&gt;Constructors and attributes&lt;/li&gt;
&lt;li&gt;Methods&lt;/li&gt;
&lt;li&gt;Object relationships&lt;/li&gt;
&lt;li&gt;Passing dependencies between objects&lt;/li&gt;
&lt;li&gt;SQLite tables&lt;/li&gt;
&lt;li&gt;Primary keys&lt;/li&gt;
&lt;li&gt;CRUD operations&lt;/li&gt;
&lt;li&gt;SQL queries&lt;/li&gt;
&lt;li&gt;Multi-module Python projects&lt;/li&gt;
&lt;li&gt;Debugging&lt;/li&gt;
&lt;li&gt;Git and GitHub&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;what-i-didn-t-focus-on-in-v1&quot;&gt;What I Didn't Focus On in V1&lt;/h3&gt;
&lt;p&gt;I deliberately didn't try to make V1 handle every possible invalid input or edge case.&lt;/p&gt;
&lt;p&gt;Comprehensive validation and robust error handling were not the main goals of this version.&lt;/p&gt;
&lt;p&gt;Instead, I decided to make the &lt;strong&gt;core functionality work first&lt;/strong&gt; and move robustness-related improvements into V2.&lt;/p&gt;
&lt;p&gt;This gives the project a clearer progression:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;V1
Core Working Stage
       ↓
V2
Robustness + Additional Core Operation
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;what-s-coming-in-v2-&quot;&gt;What's Coming in V2?&lt;/h3&gt;
&lt;p&gt;Now that the core working stage is complete, V2 will focus on improving the application's robustness and functionality.&lt;/p&gt;
&lt;p&gt;The planned V2 work includes:&lt;/p&gt;
&lt;h4 id=&quot;validation&quot;&gt;Validation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Validating user input&lt;/li&gt;
&lt;li&gt;Validating expense IDs&lt;/li&gt;
&lt;li&gt;Handling invalid values&lt;/li&gt;
&lt;li&gt;Handling invalid menu input&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;error-handling&quot;&gt;Error Handling&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Handling invalid user input gracefully&lt;/li&gt;
&lt;li&gt;Handling invalid/non-existent expense IDs&lt;/li&gt;
&lt;li&gt;Handling database-related errors&lt;/li&gt;
&lt;li&gt;Providing better user-facing error messages&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;edit-expense&quot;&gt;Edit Expense&lt;/h4&gt;
&lt;p&gt;V2 will also introduce an &lt;strong&gt;Edit Expense&lt;/strong&gt; operation.&lt;/p&gt;
&lt;p&gt;The core expense operations will then become:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Create
Read
Update
Delete
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or simply:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;CRUD
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will make the expense management functionality more complete.&lt;/p&gt;
&lt;h3 id=&quot;v1-v2&quot;&gt;V1 → V2&lt;/h3&gt;
&lt;p&gt;The project's progression can be summarized as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;V1
│
├── Add Expense
├── View Expenses
├── Delete Expense
├── SQLite persistence
├── Layered architecture
└── Core application flow
        │
        ▼
V2
│
├── Edit Expense
├── Input validation
├── ID validation
├── Error handling
└── CLI refinement
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I intentionally want to keep these stages separate.&lt;/p&gt;
&lt;p&gt;V1 proves that the &lt;strong&gt;foundation works&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;V2 will make that foundation &lt;strong&gt;more robust and complete&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;final-thoughts-1&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;This project started as a learning exercise to understand Python OOP and SQLite by building something practical.&lt;/p&gt;
&lt;p&gt;Completing V1 gave me more than just another project for my GitHub repository.&lt;/p&gt;
&lt;p&gt;It helped me understand how a small application can be divided into responsibilities and how those components communicate with each other.&lt;/p&gt;
&lt;p&gt;I'm treating V1 as a &lt;strong&gt;completed learning milestone&lt;/strong&gt;, not as the final state of the application.&lt;/p&gt;
&lt;p&gt;The core working stage is done.&lt;/p&gt;
&lt;h3 id=&quot;project-1&quot;&gt;Project&lt;/h3&gt;
&lt;p&gt; &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/xDK0d3r/personal-expense-tracker&quot;&gt;Personal Expense Tracker&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now it's time to build on that foundation.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;V1: Complete.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Core working stage: Achieved.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Next: V2 — Validation, Error Handling &amp;amp; Edit Expense.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28203/personal-expense-tracker-v1-complete-building-the-core-working-stage</guid>
<pubDate>Tue, 22 Sep 2026 17:10:47 +0000</pubDate>
</item>
<item>
<title>Building Process Mind: A Deterministic Operating System Algorithm Simulator</title>
<link>https://coderlegion.com/28149/building-process-mind-a-deterministic-operating-system-algorithm-simulator</link>
<description>&lt;p&gt;I built &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://process-mind.vercel.app/&quot;&gt;Process Mind&lt;/a&gt;&lt;/strong&gt; because I wanted a better way to practice Operating System algorithms.&lt;/p&gt;
&lt;p&gt;When preparing for exams, I often found myself solving the same types of scheduling, page replacement, disk scheduling, and memory allocation problems repeatedly. The problem wasn't finding explanations of these algorithms. There are plenty of them.&lt;/p&gt;
&lt;p&gt;The problem was &lt;strong&gt;verification&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For a manually solved problem, it is easy to make a small mistake somewhere in the calculation and only realize it much later. I wanted something where I could enter the same problem, run the algorithm, and immediately compare my result with a deterministic implementation.&lt;/p&gt;
&lt;p&gt;That idea became Process Mind.&lt;/p&gt;
&lt;h3 id=&quot;what-is-process-mind-&quot;&gt;What is Process Mind?&lt;/h3&gt;
&lt;p&gt;Process Mind is an interactive web platform for practicing Operating System algorithms.&lt;/p&gt;
&lt;p&gt;The current implementation covers four major areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CPU Scheduling&lt;/strong&gt; — FCFS, SJF, SRTF, Priority, Preemptive Priority, Round Robin&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Page Replacement&lt;/strong&gt; — FIFO, LRU, Optimal, Second Chance, LRU Counter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Disk Scheduling&lt;/strong&gt; — FCFS, SSTF, SCAN, C-SCAN, LOOK, C-LOOK&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory Allocation&lt;/strong&gt; — First Fit, Best Fit, Worst Fit, Next Fit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal isn't to simply display the final answer.&lt;/p&gt;
&lt;p&gt;The application takes the user's input, validates it, runs the corresponding algorithm, and produces structured results such as calculated metrics, process details, Gantt blocks, and execution steps.&lt;/p&gt;
&lt;p&gt;The basic flow is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;User Input
    ↓
Validation
    ↓
Algorithm
    ↓
Structured Result
    ↓
Metrics + Gantt + Process Details + Execution Steps
    ↓
UI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This separation became one of the most important design decisions in the project.&lt;/p&gt;
&lt;h3 id=&quot;the-core-idea-deterministic-results&quot;&gt;The Core Idea: Deterministic Results&lt;/h3&gt;
&lt;p&gt;There are already many tools that can explain Operating System concepts using AI or generate solutions dynamically.&lt;/p&gt;
&lt;p&gt;I wanted Process Mind to solve a different problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If I enter a problem, I should get the same result every time.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For algorithm practice, determinism is useful. The application isn't trying to interpret what I meant or generate a plausible explanation. The algorithm implementation processes the input according to its defined rules.&lt;/p&gt;
&lt;p&gt;That makes it useful for checking manually solved questions.&lt;/p&gt;
&lt;p&gt;For example, with a CPU scheduling problem, Process Mind can calculate:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Process &lt;/th&gt;&lt;th&gt;Arrival &lt;/th&gt;&lt;th&gt;Burst &lt;/th&gt;&lt;th&gt;Completion &lt;/th&gt;&lt;th&gt;Turnaround &lt;/th&gt;&lt;th&gt;Waiting &lt;/th&gt;&lt;th&gt;Response&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;P1      &lt;/td&gt;&lt;td&gt;      0 &lt;/td&gt;&lt;td&gt;    7 &lt;/td&gt;&lt;td&gt;         7 &lt;/td&gt;&lt;td&gt;         7 &lt;/td&gt;&lt;td&gt;      0 &lt;/td&gt;&lt;td&gt;       0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;P2      &lt;/td&gt;&lt;td&gt;      1 &lt;/td&gt;&lt;td&gt;    1 &lt;/td&gt;&lt;td&gt;         8 &lt;/td&gt;&lt;td&gt;         7 &lt;/td&gt;&lt;td&gt;      6 &lt;/td&gt;&lt;td&gt;       6&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;P3      &lt;/td&gt;&lt;td&gt;      2 &lt;/td&gt;&lt;td&gt;    7 &lt;/td&gt;&lt;td&gt;        15 &lt;/td&gt;&lt;td&gt;        13 &lt;/td&gt;&lt;td&gt;      6 &lt;/td&gt;&lt;td&gt;       6&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;It can also calculate average turnaround, waiting, and response times and represent execution using a Gantt chart.&lt;/p&gt;
&lt;p&gt;The exact numbers depend on the input, but the important part is that the result comes directly from the algorithm.&lt;/p&gt;
&lt;h3 id=&quot;the-difficult-part-wasn-t-writing-the-algorithms&quot;&gt;The Difficult Part Wasn't Writing the Algorithms&lt;/h3&gt;
&lt;p&gt;Implementing FCFS or SJF isn't particularly difficult once the algorithm is understood.&lt;/p&gt;
&lt;p&gt;The harder question was:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What information should the algorithm return?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Initially, it would have been easy to return something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completionTime
waitingTime
turnaroundTime
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But that isn't enough for an educational tool.&lt;/p&gt;
&lt;p&gt;If the purpose is practice and verification, users may also want to understand &lt;em&gt;why&lt;/em&gt; a process was selected.&lt;/p&gt;
&lt;p&gt;So the result structure evolved to contain more information:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;ScheduledResult
├── processes
├── metrics
├── blocks
└── steps
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;steps&lt;/code&gt; data became particularly useful.&lt;/p&gt;
&lt;p&gt;Each execution step can contain information such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;current time&lt;/li&gt;
&lt;li&gt;ready processes&lt;/li&gt;
&lt;li&gt;selected process&lt;/li&gt;
&lt;li&gt;waiting queue&lt;/li&gt;
&lt;li&gt;processes that arrived during execution&lt;/li&gt;
&lt;li&gt;queue after execution&lt;/li&gt;
&lt;li&gt;execution window&lt;/li&gt;
&lt;li&gt;whether the CPU was idle&lt;/li&gt;
&lt;li&gt;reason for selecting the process&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This allows the UI to show not only &lt;strong&gt;what happened&lt;/strong&gt;, but also some of the state behind the decision.&lt;/p&gt;
&lt;h3 id=&quot;handling-preemptive-scheduling&quot;&gt;Handling Preemptive Scheduling&lt;/h3&gt;
&lt;p&gt;Preemptive algorithms introduced another layer of complexity.&lt;/p&gt;
&lt;p&gt;For algorithms such as &lt;strong&gt;SRTF&lt;/strong&gt; and &lt;strong&gt;Preemptive Priority Scheduling&lt;/strong&gt;, a process can be interrupted when another process becomes eligible.&lt;/p&gt;
&lt;p&gt;Instead of treating a process as simply &quot;running&quot; or &quot;finished&quot;, the implementation needs to maintain its &lt;strong&gt;remaining execution time&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Process
├── arrivalTime
├── burstTime
└── remainingTime
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At each relevant point, the algorithm can determine which process should run based on the current state.&lt;/p&gt;
&lt;p&gt;Round Robin has a different challenge because execution is controlled by a &lt;strong&gt;time quantum&lt;/strong&gt;. Processes that don't finish within their allocated quantum need to be placed back into the ready queue.&lt;/p&gt;
&lt;p&gt;These differences meant that I couldn't force every scheduling algorithm into exactly the same internal implementation.&lt;/p&gt;
&lt;p&gt;Instead, I kept the implementations independent while giving them a consistent output structure.&lt;/p&gt;
&lt;p&gt;That gave me a useful balance:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;different algorithms internally, consistent results externally.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;validation-matters-more-than-it-looks&quot;&gt;Validation Matters More Than It Looks&lt;/h3&gt;
&lt;p&gt;Since users can enter arbitrary values, validation is an important part of the application.&lt;/p&gt;
&lt;p&gt;I'm using &lt;strong&gt;React Hook Form&lt;/strong&gt; with &lt;strong&gt;Zod&lt;/strong&gt; for form handling and validation.&lt;/p&gt;
&lt;p&gt;Inputs such as arrival time, burst time, priority, and quantum have appropriate constraints. Duplicate process IDs are also rejected.&lt;/p&gt;
&lt;p&gt;Random input generation is included as well.&lt;/p&gt;
&lt;p&gt;Instead of manually creating a new set of processes every time, users can generate random inputs and immediately practice with them.&lt;/p&gt;
&lt;p&gt;This fits the original reason I started building the project: &lt;strong&gt;generate a problem, solve it yourself, and verify the result.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;building-the-interface-around-the-algorithm&quot;&gt;Building the Interface Around the Algorithm&lt;/h3&gt;
&lt;p&gt;One thing I learned while building Process Mind is that an algorithm implementation and an educational interface are two different problems.&lt;/p&gt;
&lt;p&gt;The algorithm knows what happened.&lt;/p&gt;
&lt;p&gt;The UI needs to decide &lt;strong&gt;how that information should be understood by a human&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For CPU scheduling, I currently use a combination of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;input forms&lt;/li&gt;
&lt;li&gt;result metrics&lt;/li&gt;
&lt;li&gt;process tables&lt;/li&gt;
&lt;li&gt;Gantt charts&lt;/li&gt;
&lt;li&gt;expandable execution steps&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Gantt chart gives a quick overview of execution, while the execution-step section exposes more detailed information when needed.&lt;/p&gt;
&lt;p&gt;I deliberately kept the current visualization relatively simple. The larger goal is to eventually make Process Mind feel more like a simulation, but I don't want to add visual complexity just for the sake of having animations.&lt;/p&gt;
&lt;h3 id=&quot;what-i-learned-1&quot;&gt;What I Learned&lt;/h3&gt;
&lt;p&gt;The biggest lesson from Process Mind wasn't about Next.js or TypeScript.&lt;/p&gt;
&lt;p&gt;It was about &lt;strong&gt;designing data around the problem domain&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When building a normal CRUD application, the data model is often fairly obvious.&lt;/p&gt;
&lt;p&gt;With algorithms, it isn't.&lt;/p&gt;
&lt;p&gt;You have to decide what intermediate state matters, what information helps explain a decision, which metrics are useful, and how that information can be represented consistently across different algorithms.&lt;/p&gt;
&lt;p&gt;I also learned that implementing an algorithm in code forces you to understand it differently.&lt;/p&gt;
&lt;p&gt;Writing SRTF, for example, isn't the same as memorizing that &quot;the process with the shortest remaining time executes.&quot; You have to think about arrivals, remaining time, preemption, completion, ties, queues, and every state transition.&lt;/p&gt;
&lt;p&gt;That was one of the reasons I wanted to build Process Mind in the first place.&lt;/p&gt;
&lt;h3 id=&quot;what-s-next--1&quot;&gt;What's Next?&lt;/h3&gt;
&lt;p&gt;Process Mind is still evolving.&lt;/p&gt;
&lt;p&gt;The current focus is making the existing algorithms reliable and useful for practice rather than continuously adding features.&lt;/p&gt;
&lt;p&gt;The longer-term direction is to make the experience more simulation-oriented, with better visualization and interaction around algorithm execution.&lt;/p&gt;
&lt;p&gt;But the core idea remains the same:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Practice the problem yourself. Run it through the algorithm. Verify your result. Understand where the execution came from.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That's what Process Mind is trying to become—a practical bridge between &lt;strong&gt;studying an algorithm and actually understanding how it executes.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://process-mind.vercel.app/&quot;&gt;Try Process Mind&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28149/building-process-mind-a-deterministic-operating-system-algorithm-simulator</guid>
<pubDate>Tue, 22 Sep 2026 11:29:23 +0000</pubDate>
</item>
<item>
<title>⚛️ Controlled vs Uncontrolled Components in React – A Deep Dive</title>
<link>https://coderlegion.com/27766/controlled-vs-uncontrolled-components-in-react-a-deep-dive</link>
<description>&lt;p&gt;When building forms in React, you’ll inevitably come across &lt;strong&gt;controlled&lt;/strong&gt; and &lt;strong&gt;uncontrolled&lt;/strong&gt; components.&lt;br&gt;
At first glance, they look similar — both accept user input and manage form data.&lt;br&gt;
But how they manage that data under the hood can significantly impact &lt;strong&gt;performance, maintainability and UX&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;In this blog, let’s break down the difference, see them in action, explore real-world issues (like cursor position and undo/redo bugs), and understand when to use each.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-are-controlled-components-&quot;&gt;🧠 What Are Controlled Components?&lt;/h3&gt;
&lt;p&gt;In &lt;strong&gt;controlled components&lt;/strong&gt;, React state is the &lt;strong&gt;single source of truth&lt;/strong&gt;.&lt;br&gt;
That means every keystroke or change updates the component’s state and that state determines what’s rendered.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useState } from &quot;react&quot;;

function ControlledInput() {
  const [name, setName] = useState(&quot;&quot;);

  return (
    &amp;lt;input
      value={name}
      onChange={(e) =&amp;gt; setName(e.target.value)}
      placeholder=&quot;Type your name&quot;
    /&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; value is fully controlled by React via the &lt;code&gt;name&lt;/code&gt; state.&lt;br&gt;
Each keystroke triggers &lt;code&gt;setName()&lt;/code&gt;, re-rendering the component with the updated value.&lt;/p&gt;
&lt;p&gt;✅ &lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easy to validate or transform input on change.&lt;/li&gt;
&lt;li&gt;Centralized state makes debugging and unit testing easier.&lt;/li&gt;
&lt;li&gt;Integrates seamlessly with complex UIs or form libraries (Formik, React Hook Form).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frequent re-renders can impact performance in large forms.&lt;/li&gt;
&lt;li&gt;Need to carefully manage performance and input lag.&lt;/li&gt;
&lt;li&gt;Cursor position bugs can occur with advanced state management.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-are-uncontrolled-components-&quot;&gt;🧩 What Are Uncontrolled Components?&lt;/h3&gt;
&lt;p&gt;In &lt;strong&gt;uncontrolled components&lt;/strong&gt;, the DOM maintains its own state — React just &lt;strong&gt;references&lt;/strong&gt; it.&lt;br&gt;
You don’t handle every keystroke; instead, you access the value when needed.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useRef } from &quot;react&quot;;

function UncontrolledInput() {
  const inputRef = useRef();

  const handleSubmit = () =&amp;gt; {
    alert(`Input value: ${inputRef.current.value}`);
  };

  return (
    &amp;lt;&amp;gt;
      &amp;lt;input ref={inputRef} placeholder=&quot;Type your name&quot; /&amp;gt;
      &amp;lt;button onClick={handleSubmit}&amp;gt;Submit&amp;lt;/button&amp;gt;
    &amp;lt;/&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;✅ &lt;strong&gt;Pros&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Better performance for simple forms.&lt;/li&gt;
&lt;li&gt;Minimal React re-renders.&lt;/li&gt;
&lt;li&gt;Perfect when you just need the final value.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Cons&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Harder to validate or modify values dynamically.&lt;/li&gt;
&lt;li&gt;Not ideal when input data must always reflect application state.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-controlled-vs-uncontrolled-key-differences&quot;&gt;⚔️ Controlled vs Uncontrolled: Key Differences&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Feature         &lt;/th&gt;&lt;th&gt;Controlled                 &lt;/th&gt;&lt;th&gt;Uncontrolled&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Data Source&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;React State                &lt;/td&gt;&lt;td&gt;DOM (via refs)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;May re-render often        &lt;/td&gt;&lt;td&gt;Faster for simple inputs&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Validation&lt;/strong&gt;  &lt;/td&gt;&lt;td&gt;Easy to handle dynamically &lt;/td&gt;&lt;td&gt;Requires manual check&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Use Case&lt;/strong&gt;    &lt;/td&gt;&lt;td&gt;Complex, validated forms   &lt;/td&gt;&lt;td&gt;Simple or performance-critical inputs&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-valtio-amp-controlled-inputs-why-cursor-jumps-happen&quot;&gt;⚡ Valtio &amp;amp; Controlled Inputs: Why Cursor Jumps Happen&lt;/h3&gt;
&lt;p&gt;When you use a state library like Valtio in a React controlled input, you might encounter this issue: you edit text in the middle of the input, but the cursor jumps to the end. This frustrates users and breaks the typing experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;🔍 What’s going on?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;By default, Valtio batches updates before triggering re-renders. The documentation states:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“By default, state mutations are batched before triggering re-render. Sometimes, we want to disable the batching.”&lt;br&gt;
Because of this batching behavior, when you type and update the proxy state, multiple changes may be queued and then re-rendered in one go. During that delay, the browser loses context of the input cursor position, so after render the input’s value is applied and the cursor jumps to the end.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;✅ The fix: { sync: true }&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Valtio provides the option to disable batching for specific use-cases like inputs:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;const snap = useSnapshot(state, { sync: true });&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;sync: true&lt;/code&gt;, updates go through immediately rather than being deferred/batched. This keeps the input cursor where the user expects it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Trade-off:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;sync: true&lt;/code&gt; disables batching optimizations, which can reduce render efficiency in large/complex components. Therefore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;sync: true&lt;/code&gt; only in input fields where cursor behaviour matters.&lt;/li&gt;
&lt;li&gt;Avoid it for general state where performance is more important than instantaneous update.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-undo-redo-cmd-z-cmd-shift-z-in-controlled-components&quot;&gt;🔁 Undo/Redo (Cmd+Z / Cmd+Shift+Z) in Controlled Components&lt;/h3&gt;
&lt;p&gt;Another subtle but important issue:&lt;br&gt;
Undo (&lt;code&gt;Cmd+Z&lt;/code&gt;) and Redo (&lt;code&gt;Cmd+Shift+Z&lt;/code&gt;) &lt;strong&gt;might not work&lt;/strong&gt; as expected in controlled inputs.&lt;/p&gt;
&lt;p&gt;Why?&lt;br&gt;
Because React replaces the entire value on every keystroke rather than letting the browser handle native input history.&lt;/p&gt;
&lt;p&gt;This means the browser sometimes &lt;strong&gt;loses the input undo stack&lt;/strong&gt;, especially if re-renders happen between updates.&lt;/p&gt;
&lt;p&gt;🧪 &lt;strong&gt;What to do:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Test undo/redo behavior in all input fields before handing over to QA.&lt;/li&gt;
&lt;li&gt;If using a state library or custom hooks, check whether it maintains the input history properly.&lt;/li&gt;
&lt;li&gt;In cases where it fails and browser-native undo is critical (e.g., long text editors), consider using &lt;strong&gt;uncontrolled inputs&lt;/strong&gt; or controlled wrappers that debounce updates.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-best-practices-for-react-forms&quot;&gt;🧰 Best Practices for React Forms&lt;/h3&gt;
&lt;p&gt;✅ Use &lt;strong&gt;controlled inputs&lt;/strong&gt; when you need validation, dynamic control or shared state.&lt;br&gt;
✅ Use &lt;strong&gt;uncontrolled inputs&lt;/strong&gt; when performance and simplicity matter.&lt;br&gt;
✅ For state libraries like &lt;strong&gt;Valtio&lt;/strong&gt;, test cursor and undo behaviors thoroughly.&lt;br&gt;
✅ Avoid unnecessary re-renders – use &lt;code&gt;React.memo&lt;/code&gt;, &lt;code&gt;useCallback&lt;/code&gt; or split components.&lt;br&gt;
✅ Always test form UX — typing, cursor and undo/redo should feel &lt;strong&gt;native and fluid&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-final-thoughts&quot;&gt;🎯 Final Thoughts&lt;/h3&gt;
&lt;p&gt;Controlled vs Uncontrolled Components seem like a small React concept, but they can deeply impact user experience, especially in &lt;strong&gt;complex form-heavy applications&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;💡 Understanding these nuances (like cursor handling, undo/redo, and batching) helps you ship &lt;strong&gt;smoother, bug-free UIs&lt;/strong&gt; that feel professional and reliable.&lt;/p&gt;
&lt;p&gt;🚀 Whether you use plain React or libraries like Valtio, always test the real typing experience — that’s where users feel the difference.&lt;/p&gt;
&lt;p&gt;💬 Have you ever faced cursor or undo issues in your React forms? Drop your experience or workaround below — let’s learn together! 👇&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27766/controlled-vs-uncontrolled-components-in-react-a-deep-dive</guid>
<pubDate>Sat, 19 Sep 2026 19:08:29 +0000</pubDate>
</item>
<item>
<title>Building Before I Feel Ready</title>
<link>https://coderlegion.com/27485/building-before-i-feel-ready</link>
<description>&lt;p&gt;There’s a strange feeling that comes with learning technology.&lt;/p&gt;
&lt;p&gt;You can spend hours watching tutorials, reading documentation, taking courses, and solving problems and still feel like you don't know enough to build something real.&lt;/p&gt;
&lt;p&gt;That feeling has followed me for a while.&lt;/p&gt;
&lt;p&gt;I'm studying Electronic and Electrical Engineering, while also learning frontend development, IoT, and embedded systems. One day I'm working with React and TypeScript. The next, I'm looking at microcontrollers, sensors, circuits, or trying to figure out why some piece of hardware isn't behaving the way I expected.&lt;/p&gt;
&lt;p&gt;There is always something else to learn.&lt;/p&gt;
&lt;p&gt;And that's the problem.&lt;/p&gt;
&lt;p&gt;For a while, I kept thinking I needed to learn more before I could seriously start building.&lt;/p&gt;
&lt;p&gt;I eventually realized that was backwards.&lt;/p&gt;
&lt;p&gt;Maybe building is how I learn what I actually need to know.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;strong&gt;The Trap of Always Learning&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p&gt;The problem with always preparing is that there is never a clear point where you are “ready.”&lt;/p&gt;
&lt;p&gt;There is always another course to finish.&lt;/p&gt;
&lt;p&gt;Another framework to learn.&lt;/p&gt;
&lt;p&gt;Another concept you don't fully understand.&lt;/p&gt;
&lt;p&gt;Another project that seems slightly beyond your current skill level.&lt;/p&gt;
&lt;p&gt;And because there is always something else to learn, it becomes easy to confuse learning with progress.&lt;/p&gt;
&lt;p&gt;I've fallen into that trap myself.&lt;/p&gt;
&lt;p&gt;I'd jump between frontend development, engineering courses, IoT, programming, and different ideas I wanted to explore. I was learning, but sometimes it felt like I was constantly starting rather than actually moving forward.&lt;/p&gt;
&lt;p&gt;That's when I started changing how I approach learning.&lt;/p&gt;
&lt;p&gt;Instead of asking:&lt;/p&gt;
&lt;p&gt;“What do I need to learn before I can build this?”&lt;/p&gt;
&lt;p&gt;I started asking:&lt;/p&gt;
&lt;p&gt;“What do I need to learn to solve the next problem?”&lt;/p&gt;
&lt;p&gt;That small change makes building much less intimidating.&lt;/p&gt;
&lt;p&gt;You don't need to know everything.&lt;/p&gt;
&lt;p&gt;You need to know enough to take the next step.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;Building Forces You to Learn Differently&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
Tutorials are useful. Courses are useful. Documentation is useful.&lt;/p&gt;
&lt;p&gt;But building something of your own changes the learning experience completely.&lt;/p&gt;
&lt;p&gt;When you're following a tutorial, the path is already laid out. You know what comes next, what code to write, and often what the expected result should look like.&lt;/p&gt;
&lt;p&gt;When you're building your own project, none of that is guaranteed.&lt;/p&gt;
&lt;p&gt;You make decisions.&lt;/p&gt;
&lt;p&gt;You run into errors you have never seen before.&lt;/p&gt;
&lt;p&gt;You search through documentation.&lt;/p&gt;
&lt;p&gt;You try something that doesn't work.&lt;/p&gt;
&lt;p&gt;You change your approach.&lt;/p&gt;
&lt;p&gt;Then, eventually, something works.&lt;/p&gt;
&lt;p&gt;That's where I think some of the most valuable learning happens.&lt;/p&gt;
&lt;p&gt;For example, while working on IoT projects, I’ve had to move between hardware and software, dealing with sensors and microcontrollers on one side and interfaces and data visualization on the other.&lt;/p&gt;
&lt;p&gt;The interesting part isn't knowing every technology beforehand.&lt;/p&gt;
&lt;p&gt;It's figuring out how to connect the pieces.&lt;/p&gt;
&lt;p&gt;And every problem becomes a question:&lt;/p&gt;
&lt;p&gt;Why isn't this working?&lt;/p&gt;
&lt;p&gt;Then you look for the answer.&lt;/p&gt;
&lt;p&gt;That process has taught me something important:&lt;/p&gt;
&lt;p&gt;You don't always need more preparation. Sometimes you need a problem worth solving.&lt;/p&gt;
&lt;p&gt;&lt;u&gt;&lt;strong&gt;I'm Still Figuring Things Out&lt;/strong&gt;&lt;/u&gt;&lt;br&gt;
I don't want this to sound like I suddenly figured everything out.&lt;/p&gt;
&lt;p&gt;I haven't.&lt;/p&gt;
&lt;p&gt;I'm still learning how to write better code, design better interfaces, understand engineering concepts more deeply, and build projects that actually work outside of a tutorial.&lt;/p&gt;
&lt;p&gt;Some projects will fail.&lt;/p&gt;
&lt;p&gt;Some ideas will change halfway through.&lt;/p&gt;
&lt;p&gt;Some things I build will probably look very different a few months from now.&lt;/p&gt;
&lt;p&gt;And I think that's okay.&lt;/p&gt;
&lt;p&gt;I'm sharing more of what I build because I want a record of the process the messy middle- not just the polished result.&lt;/p&gt;
&lt;p&gt;The bugs.&lt;/p&gt;
&lt;p&gt;The things I misunderstood.&lt;/p&gt;
&lt;p&gt;The solutions I eventually found.&lt;/p&gt;
&lt;p&gt;The decisions that worked and the ones that didn't.&lt;/p&gt;
&lt;p&gt;I also want to meet other people who are doing the same thing: learning by building.&lt;/p&gt;
&lt;p&gt;Because something is motivating about seeing someone else struggle through a problem, solve it, and then share what they learned.&lt;/p&gt;
&lt;p&gt;That's the kind of developer and engineer I want to become.&lt;/p&gt;
&lt;p&gt;Not someone who knows everything.&lt;/p&gt;
&lt;p&gt;Someone who keeps building, keeps learning, and knows how to figure things out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;What I'm Building Now&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
Right now, I'm exploring the space where software and hardware meet.&lt;/p&gt;
&lt;p&gt;On the software side, I'm building with technologies like React and TypeScript, learning how to create interfaces that are not just functional but actually enjoyable to use.&lt;/p&gt;
&lt;p&gt;On the hardware side, I'm experimenting with microcontrollers, sensors, and IoT projects. That means dealing with things that don't always behave exactly as expected—which is a pretty effective way to learn.&lt;/p&gt;
&lt;p&gt;I'm also studying Electronic and Electrical Engineering, so I'm constantly discovering connections between what I learn in class and what I can actually build.&lt;/p&gt;
&lt;p&gt;That's probably what interests me most.&lt;/p&gt;
&lt;p&gt;A web application doesn't have to exist completely separately from the physical world.&lt;/p&gt;
&lt;p&gt;A sensor can collect data.&lt;/p&gt;
&lt;p&gt;A microcontroller can process it.&lt;/p&gt;
&lt;p&gt;A backend can move it somewhere useful.&lt;/p&gt;
&lt;p&gt;And a frontend can turn that data into something a person can actually understand and interact with.&lt;/p&gt;
&lt;p&gt;There's a lot I still don't know about all of this.&lt;/p&gt;
&lt;p&gt;But that's exactly why I'm building.&lt;/p&gt;
&lt;p&gt;I want to see how far I can go by learning one problem at a time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;So, What Now?&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
I don't have a perfectly defined roadmap.&lt;/p&gt;
&lt;p&gt;And honestly, I'm starting to think that's okay.&lt;/p&gt;
&lt;p&gt;For now, I want to keep building projects, documenting what I learn, and gradually getting better at connecting the different things I'm interested in.&lt;/p&gt;
&lt;p&gt;Some posts here will probably be about frontend development.&lt;/p&gt;
&lt;p&gt;Others might be about IoT, embedded systems, engineering concepts, or something completely different that I happen to be exploring.&lt;/p&gt;
&lt;p&gt;I also want to share the parts that don't usually make it into portfolios—the debugging, the mistakes, the confusion, and the small breakthroughs that make a project finally work.&lt;/p&gt;
&lt;p&gt;Maybe someone reading this is also stuck in the “I'm not ready yet” phase.&lt;/p&gt;
&lt;p&gt;If that's you, I'm learning the same lesson myself:&lt;/p&gt;
&lt;p&gt;You don't have to wait until you know everything.&lt;/p&gt;
&lt;p&gt;Start with what you know.&lt;/p&gt;
&lt;p&gt;Build something.&lt;/p&gt;
&lt;p&gt;When you hit a problem, learn what you need to solve it.&lt;/p&gt;
&lt;p&gt;Then build again.&lt;/p&gt;
&lt;p&gt;That's what I'm going to do.&lt;/p&gt;
&lt;p&gt;Build before I'm ready. Learn along the way. And keep going.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;u&gt;I'm Just Getting Started&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is my first post on DEV.to, and I think it should start here.&lt;/p&gt;
&lt;p&gt;I'm not writing because I have everything figured out. I'm writing because I want to document the process of figuring things out.&lt;/p&gt;
&lt;p&gt;I'll be sharing projects, experiments, lessons, mistakes, and whatever interesting problems I run into along the way.&lt;/p&gt;
&lt;p&gt;If you're also learning to code, studying engineering, building projects, or trying to figure out how all these pieces fit together, maybe we can learn from each other.&lt;/p&gt;
&lt;p&gt;For now, I'm going to keep building.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;One project. One problem. One lesson at a time.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27485/building-before-i-feel-ready</guid>
<pubDate>Thu, 17 Sep 2026 08:41:43 +0000</pubDate>
</item>
<item>
<title>From Pro Disc Golf to Code: My Journey to Become a Full-Stack Developer</title>
<link>https://coderlegion.com/26756/from-pro-disc-golf-to-code-my-journey-to-become-a-full-stack-developer</link>
<description>&lt;p&gt;A year ago I was walking new construction homes as a field manager. I managed the installation of tile wood and carpet in new construction homes. It’s served my family and I well, and put me in situations of self growth I would have never gotten otherwise. But I hit a wall and upward mobility was stagnant. Before I was a field manager I was a professional disc golfer. It was always an adventure, I got to tour the country playing tournaments and teaching clinics. That era instilled in me a deep understanding of planning, discipline, and the power of creating systems to excel.&lt;/p&gt;
&lt;p&gt;Current day… I am obsessed with coding. My mind is always buzzing with what I am currently learning, and what technical puzzle I am solving at the time. This passion ignited on a whim last year. I was sent a link to web development coding bootcamp from a family friend. In a whirlwind I called my amazingly supportive wife, and within 20 minutes I was enrolled in the bootcamp. Class started that same day. I had a buzzing in my body, that had me excited and scared all at the same time. &lt;/p&gt;
&lt;p&gt;The challenges, the creativity, and the complex problem-solving drove me to fall deep into coding. This post marks the beginning of me sharing my journey. From fairway to construction site to becoming a full stack developer. &lt;/p&gt;
&lt;p&gt;Graduating bootcamp last June, the pressure to secure a job intensified with our first child due in February. Despite relentless effort, not a single interview materialized between June and August. Providing for my family meant &quot;biting the bullet&quot; and returning to construction. It felt like taking a quadruple bogey on the tournament's final hole, but just like in disc golf, mental resilience became key. I pivoted. I recognized my skills weren't &quot;up to snuff,&quot; especially with AI raising the industry bar. My love for coding made the decision clear: Rise and grind.&lt;/p&gt;
&lt;p&gt;That &quot;quadruple bogey&quot; taught me not to let one bad hole ruin the entire round. I went back into the lab and re-evaluated my coding habits and systems, realizing there was immense room for improvement. The &quot;get rich quick&quot; mentality wasn't working. My focus shifted to a long-term, sustainable journey of growth.&lt;/p&gt;
&lt;p&gt;One year into this revised plan, I'm already seeing progress. I'm currently interning, building the syllabus website for the very bootcamp I attended – a full-circle moment. This valuable experience, coupled with freelance work and my dedicated side project, is where I'm gaining real-world skills. My current side project, a Disc Golf Course and Round Tracker, directly reinforces my internship learnings in PHP and SQL, allowing me to immediately apply and practice what I'm currently using.&lt;/p&gt;
&lt;p&gt;While PHP and SQL aren't my primary languages, this hands-on experience has been truly eye-opening, revealing just how much React streamlines development under the hood. Building reusable OOP components in PHP has definitely made me itch to return to React's ecosystem! Once this internship concludes, my focus shifts to an even bigger project I'm eager to tackle: a MERN stack application.&lt;/p&gt;
&lt;p&gt;The road from professional disc golf and construction management to becoming a full-stack developer is proving to be a true test of grit and adaptability. It's a journey I'm embracing fully, learning new languages and paradigms with every step. I'm committed to building a solid portfolio and making this career transition a success. Through this blog, I aim to share the raw, unfiltered experience the “aces” and the &quot;bogeys&quot;  if you will. I hope to connect with others on a similar path. Join me as I continue to build, learn, and grow in the world of web development.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26756/from-pro-disc-golf-to-code-my-journey-to-become-a-full-stack-developer</guid>
<pubDate>Thu, 10 Sep 2026 17:47:47 +0000</pubDate>
</item>
<item>
<title>How Online Assessments Help Students Identify Their Learning Gaps</title>
<link>https://coderlegion.com/26719/how-online-assessments-help-students-identify-their-learning-gaps</link>
<description>&lt;p&gt;You study for hours, walk into the exam feeling ready and then just blank out on questions you swear you knew, it happens to basically every student at some point, honestly. Usually it's not that you didn't study enough. It's that you studied the wrong things or you had gaps you never even knew were sitting there.&lt;/p&gt;
&lt;p&gt;This is one of the biggest problems with how most students prepare, if you think about it. They read, they revise, they feel like they've got it but knowing something while reading calmly at home is not the same as pulling that answer out under exam pressure. Most students only find this out the hard way, mid exam, when there's nothing left they can do about it.&lt;/p&gt;
&lt;p&gt;Been through this yourself? Or maybe you're a parent watching your kid go through the same thing over and over. Looking into the best platform for online courses with mock tests and assessments could genuinely change how prep goes from here. &lt;/p&gt;
&lt;p&gt;Let's get into why assessments matter this much and how they catch gaps that plain studying just misses.&lt;/p&gt;
&lt;p&gt;Why Just Studying Doesn't Show Your Weak Spots&lt;/p&gt;
&lt;p&gt;Reading and revising feels productive, no doubt. You go through your notes, highlight the important bits, feel like real progress is happening but this kind of studying doesn't actually test whether you can use what you've learned under real conditions.&lt;/p&gt;
&lt;p&gt;There's a big difference between recognising an answer when you see it and actually pulling it out of memory during a timed test. Students mix these two up all the time, thinking they're ready when they're really not. This gap only shows up once there's an actual test involved, not during casual revision on a Sunday afternoon.&lt;/p&gt;
&lt;p&gt;Another thing, students usually don't know which topics they're actually weak in until they get tested on them directly. You might feel totally solid in a chapter because it made sense while studying. Then a question comes at it from a slightly different angle, and suddenly it's not so clear anymore.&lt;/p&gt;
&lt;p&gt;This is exactly the kind of blind spot regular assessments catch. A good best platform for online courses with mock tests and assessments means you're not guessing where you stand anymore. You actually find out through real practice, way before the real exam catches you off guard.&lt;/p&gt;
&lt;p&gt;How Mock Tests Show What You Actually Know&lt;/p&gt;
&lt;p&gt;Mock tests work differently from just studying. They put you in something close to the real exam environment- time pressure, unfamiliar phrasing, needing to recall stuff fast without flipping back to your notes, all of it mirrors exam day pretty closely.&lt;/p&gt;
&lt;p&gt;Taking a mock test isn't just about checking if you know the material. It's about checking if you can actually access that knowledge when there's pressure involved. So many students find out during mock tests that they freeze up, or completely blank on things they knew perfectly well while studying at home. That's genuinely useful information, and you only get it through real testing.&lt;/p&gt;
&lt;p&gt;Mock tests also show patterns in your mistakes that you'd otherwise never notice. Maybe you keep messing up a certain type of question, not because you don't get the concept but because you keep misreading how it's framed. This kind of pattern is nearly impossible to catch without taking several tests and actually going back to review them properly.&lt;/p&gt;
&lt;p&gt;This is really where the value lies in using the best platform for online courses with mock tests and assessments on a regular basis, not just the one time right before your exam. Repeated testing shows patterns over time, patterns a single test just can't reveal on its own.&lt;/p&gt;
&lt;p&gt;Why Regular Testing Beats One Test&lt;/p&gt;
&lt;p&gt;A lot of students only sit for a mock test close to their exam date, treating it as some final check rather than a tool they use throughout. This actually limits how useful it can be, because by then there's barely any time left to fix whatever comes up.&lt;/p&gt;
&lt;p&gt;Regular assessments spread through your prep work completely differently. Instead of discovering weak spots a week before your exam, you find them out weeks, sometimes months, earlier. That gives you real time to actually work on them instead of panicking at the last minute.&lt;/p&gt;
&lt;p&gt;This kind of approach also lets you track your own improvement. Taking assessments regularly shows you whether you're genuinely getting better in your weak areas, or if the same mistakes just keep popping up again and again. You simply can't track this with one test taken right before the exam.&lt;/p&gt;
&lt;p&gt;Good platforms understand this, it is exactly why the best platform for online courses with mock tests and assessments usually has regular testing built right into the course, not some token test tacked on at the end where there's nothing left to do with the results anyway.&lt;/p&gt;
&lt;p&gt;The Confidence Some People Don't Talk About Enough&lt;/p&gt;
&lt;p&gt;There's another side to this that doesn't get discussed much. Regular assessments don't just point out where you're weak, they build real confidence in the areas where you're actually strong too and this matters way more than people give it credit for when it comes to exam day performance.&lt;/p&gt;
&lt;p&gt;A big chunk of exam anxiety just comes from uncertainty, not really knowing where you stand. &lt;/p&gt;
&lt;p&gt;Students who've never properly tested themselves walk in unsure if they're ready, and that uncertainty alone creates stress that hurts performance, even in the parts they were actually well prepared for.&lt;br&gt;
Students who've taken regular assessments though, they walk in with a much clearer picture. They know what they're good at, they've worked on what they weren't, less unknown territory to be nervous about. That kind of confidence, built through real practice instead of just hoping for the best, genuinely changes how exam day goes.&lt;/p&gt;
&lt;p&gt;This is another big reason the best platform for online courses with mock tests and assessments matters so much, not just for catching gaps but for building the kind of confidence that only comes from actually knowing where you stand.&lt;/p&gt;
&lt;p&gt;What To Actually Check In A Good Assessment Platform&lt;/p&gt;
&lt;p&gt;If you're picking a platform for this, there's a few things worth checking properly first. Make sure the mock tests genuinely match the real exam pattern, not some generic set of questions that has nothing to do with what you'll actually face on exam day.&lt;/p&gt;
&lt;p&gt;Check if the platform gives you detailed feedback after each test too, not just a score sitting there with no explanation. A number alone doesn't really tell you much, what helps is knowing exactly which topics or question types are tripping you up, so you know what to fix next.&lt;/p&gt;
&lt;p&gt;Look for platforms that offer regular assessments spread across the whole course, not just occasional ones here and there. Like mentioned earlier, consistent testing over time reveals patterns that a one off test just can't show you.&lt;/p&gt;
&lt;p&gt;And check if there's actual support to help you work on the gaps once you've found them. Finding your weak spots is honestly only half the battle, having proper guidance or resources to fix them is what makes this whole process genuinely useful, not just informative on paper.&lt;/p&gt;
&lt;p&gt;Final Thoughts: Turning Weak Spots Into Strong Ones&lt;/p&gt;
&lt;p&gt;At the end of the day, the whole point of finding your learning gaps is to actually fix them, not just know they're there. Assessments show you exactly where to put your energy, instead of spreading effort evenly across stuff you're already good at and stuff you're genuinely struggling with.&lt;br&gt;
Students who use assessments properly through their whole preparation, not just once at the very end, usually walk into exams with a much clearer sense of how ready they actually are. They've already faced their weak spots during practice, worked through them, and built confidence through repeated testing rather than just crossing their fingers.&lt;/p&gt;
&lt;p&gt;If you're serious about finding your real learning gaps and actually fixing them before the real exam shows up, ItLearn by Imbibe Tech is genuinely one of the best platform for online courses with mock tests and assessments out there right now. It gives you regular testing, detailed feedback, and real guidance to work on exactly what's holding your score back. Don't wait for exam day to figure out where you're weak. &lt;/p&gt;
&lt;p&gt;Get started with ItLearn by us today and use the &lt;a rel=&quot;nofollow&quot; href=&quot;https://imbibe.in/itlearn/&quot;&gt;best platform for online courses with mock tests and assessments&lt;/a&gt; to prepare the right way, right from the start.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26719/how-online-assessments-help-students-identify-their-learning-gaps</guid>
<pubDate>Thu, 10 Sep 2026 11:02:12 +0000</pubDate>
</item>
<item>
<title>How I Survived the Silent AI Upgrades (And Shifted My Workflow)</title>
<link>https://coderlegion.com/26629/how-i-survived-the-silent-ai-upgrades-and-shifted-my-workflow</link>
<description>&lt;h3 id=&quot;the-day-the-premium-quotas-melted&quot;&gt;The Day the Premium Quotas Melted&lt;/h3&gt;
&lt;p&gt;We’ve all seen the massive marketing headlines celebrating multi-million token context windows. So when I scored access to the &lt;strong&gt;Google AI Pro student tier&lt;/strong&gt;, I figured I was completely set.&lt;/p&gt;
&lt;p&gt;My journey into the anatomy of a prompt didn’t happen overnight; it started a while back when I was experimenting with visual generation in &lt;strong&gt;Adobe Firefly&lt;/strong&gt;. By August of last year, I got deep into language models, eventually using them to power through my &lt;strong&gt;EBAC programming course&lt;/strong&gt; and moving my coding workflows directly into &lt;strong&gt;Antigravity IDE&lt;/strong&gt;. Over nearly a year of continuous, everyday use, I learned exactly how these systems tick. As an aspiring developer, I optimized my prompts, mapped out constraints, and treated the LLM like a highly predictable engine to accelerate my learning.&lt;/p&gt;
&lt;p&gt;Naturally, I put my established setup to the test. I uploaded my personal documentation, technical archives, and coding references into the newly rebranded &lt;strong&gt;Gemini Notebook&lt;/strong&gt; (formerly NotebookLM), running Flash Extended to handle the retrieval. I wasn't even running heavy Pro-level operations—just a routine pass to extract milestones and start drafting a profile.&lt;/p&gt;
&lt;p&gt;Then, I asked a few formatting questions, checked my usage dashboard, and watched in pure disbelief as &lt;strong&gt;50% of my rolling 5-hour compute quota completely evaporated&lt;/strong&gt; in a matter of minutes.&lt;/p&gt;
&lt;p&gt;I hadn't changed my source files. I hadn't switched models mid-session. But the architectural ground shifted anyway. Here is exactly what is happening under the hood with these silent AI infrastructure rollouts, and how a junior dev's workflow had to change overnight to survive the token crunch.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-the-background-agent-tax&quot;&gt;1. The Background Agent Tax&lt;/h3&gt;
&lt;p&gt;We used to think of Large Language Models as linguistic calculators—they predicted the next most probable word based on pattern matching.&lt;/p&gt;
&lt;p&gt;But with recent infrastructure upgrades, the underlying architecture has evolved into something much more complex. When you ask a modern AI assistant a complex analytical question over your documents, it doesn't just read the text anymore. &lt;strong&gt;The system silently provisions a secure cloud computer, writes a custom Python script to parse your sources, executes it, and formats the output.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Because the Gemini ecosystem now shares parts of the same agentic code-execution harness used by &lt;strong&gt;Antigravity IDE&lt;/strong&gt;, it treats your documents like an active environment. While this makes the AI incredibly precise and practically eliminates linguistic math hallucinations, it comes with a massive &lt;strong&gt;compute surcharge&lt;/strong&gt;. Every background execution loop, sandboxed environment state, and data variable has to be held in the active context window. You aren't just paying for the words you read; you are paying the operational runtime of a background software engineer.&lt;/p&gt;
&lt;h3 id=&quot;2-aggressive-context-slicing&quot;&gt;2. Aggressive Context Slicing&lt;/h3&gt;
&lt;p&gt;To prevent web interfaces from lagging under the weight of these massive agentic loops, consumer applications are quietly implementing &lt;strong&gt;context slicing&lt;/strong&gt; and extreme text compression behind the scenes.&lt;/p&gt;
&lt;p&gt;Once your conversational history crosses a certain threshold or accumulates too many background code-execution logs, the interface stops passing the entire thread to the model. Instead, it relies on silent retrieval chunks. This is why an AI you’ve been working with seamlessly for an hour will suddenly &quot;forget&quot; a critical formatting rule or a core naming convention you established in prompt #2.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;my-new-ai-survival-blueprint-ecosystem-hopping&quot;&gt;My New AI Survival Blueprint: Ecosystem Hopping&lt;/h3&gt;
&lt;p&gt;To keep my workflows efficient and stop burning through my daily student compute caps by noon, I’ve completely restructured how I interact with LLMs. The secret? &lt;strong&gt;You have to jump across different Google products strategically to keep your tokens alive.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Separating Extraction from Iteration:&lt;/strong&gt; I no longer do creative drafting or iterative formatting inside data-dense notebooks. I use the notebook &lt;em&gt;once&lt;/em&gt; to let the agent parse the files and extract a clean, structured text baseline or chronological milestone list.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The &quot;Clear Slate&quot; Migration:&lt;/strong&gt; Once I have that raw text block, I copy it out, close the heavy workspace entirely, and paste it into a completely fresh, standard chat window. By decoupling the heavy data-parsing engine from the creative-writing layer, my compute footprint drops to near zero.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Graduating to Developer Environments:&lt;/strong&gt; For complex programming or tracking intricate data structures where memory loss is a project-killer, I step outside the consumer web wrappers and jump into &lt;strong&gt;Google AI Studio&lt;/strong&gt;. Getting raw model access gives you deterministic control over your context window and absolute visibility over your exact token count, completely bypassing the aggressive background agent surcharges.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;The Takeaway:&lt;/strong&gt; AI isn't just getting bigger; it's getting hungrier. Even if you've spent a year mastering the tool, hold premium tier access, and use it to power through coding bootcamps, if you don't actively manage your context architecture, the infrastructure will manage it for you—usually at the expense of your active memory. As developers, our job isn't just to write code; it's to understand the systems we build upon. And right now, navigating the AI layer demands a completely new style of resource management.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26629/how-i-survived-the-silent-ai-upgrades-and-shifted-my-workflow</guid>
<pubDate>Wed, 09 Sep 2026 13:24:29 +0000</pubDate>
</item>
<item>
<title>The power of mental models for developers</title>
<link>https://coderlegion.com/26608/the-power-of-mental-models-for-developers</link>
<description>&lt;h3 id=&quot;down-the-memory-lane&quot;&gt;Down the memory lane&lt;/h3&gt;
&lt;p&gt;Some developers don’t understand memory.&lt;br&gt;
They understand new, delete, or garbage collection. And again — that’s just the surface.&lt;/p&gt;
&lt;p&gt;Memory is not a flat, uniform space.&lt;br&gt;
It’s a hierarchy:&lt;br&gt;
• registers&lt;br&gt;
• multiple levels of cache&lt;br&gt;
• RAM&lt;br&gt;
• virtual memory managed by the OS&lt;br&gt;
Each layer has different latency and behavior.&lt;/p&gt;
&lt;p&gt;But we usually learn memory management like this:&lt;br&gt;
“allocate object → use it → free it”&lt;br&gt;
So we assume: as long as memory is “managed”, everything is fine.&lt;br&gt;
It’s not.&lt;/p&gt;
&lt;p&gt;Performance is often dominated by:&lt;br&gt;
• cache locality&lt;br&gt;
• allocation patterns&lt;br&gt;
• fragmentation&lt;br&gt;
• allocator contention&lt;/p&gt;
&lt;p&gt;That’s why two identical algorithms can have wildly different performance&lt;br&gt;
depending on how data is laid out in memory.&lt;/p&gt;
&lt;p&gt;Using smart pointers doesn’t mean you understand memory. It means you’ve delegated part of the problem. Which is fine — until it isn’t.&lt;/p&gt;
&lt;p&gt;If you care about performance, you need to understand:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;how data moves,&lt;/li&gt;
&lt;li&gt;how it’s cached,&lt;br&gt;
and how it’s allocated.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because in the end:&lt;br&gt;
It’s not about what your code does.&lt;br&gt;
It’s about how it lives in memory.&lt;/p&gt;
&lt;h3 id=&quot;building-blocks&quot;&gt;Building blocks&lt;/h3&gt;
&lt;p&gt;Data types are one of the most misunderstood fundamentals.&lt;br&gt;
Because we don’t really learn what they are.&lt;/p&gt;
&lt;p&gt;We learn their names: int, float, bool - feel like simple building blocks.&lt;br&gt;
But underneath, they are:&lt;br&gt;
• bit patterns&lt;br&gt;
• rules of interpretation&lt;br&gt;
• operations defined over those bits&lt;br&gt;
Without that model, decisions become guesswork.&lt;/p&gt;
&lt;p&gt;Like using signed integers for sizes.&lt;br&gt;
A size is a count. It cannot be negative.&lt;/p&gt;
&lt;p&gt;But if your type allows negative values, you’ve already introduced invalid states into your system.&lt;br&gt;
This leads to subtle bugs:&lt;br&gt;
• overflow issues&lt;br&gt;
• signed/unsigned comparison traps&lt;br&gt;
• edge cases that “should never happen” — but do&lt;/p&gt;
&lt;p&gt;The deeper issue is this:&lt;br&gt;
A type is not a label. It’s a constraint on reality.&lt;/p&gt;
&lt;p&gt;If you don’t understand:&lt;br&gt;
• binary representation&lt;br&gt;
• overflow behavior&lt;br&gt;
• how arithmetic actually works at the hardware level&lt;br&gt;
then you’re not choosing types. You’re guessing.&lt;/p&gt;
&lt;p&gt;And sometimes guessing is enough.&lt;br&gt;
Until it isn’t.&lt;/p&gt;
&lt;p&gt;We teach developers what to write.&lt;br&gt;
We rarely teach them what it means.&lt;/p&gt;
&lt;h3 id=&quot;concurrency&quot;&gt;Concurrency&lt;/h3&gt;
&lt;p&gt;Some developers don’t learn concurrency. They learn async/await or whatever concurrency utilities their favorite language or framework provides. And those are not the same thing.&lt;/p&gt;
&lt;p&gt;Threads are not a language feature. They exist at the OS level, run on physical CPU cores, and compete for shared memory and cache.&lt;/p&gt;
&lt;p&gt;But many of us first encounter concurrency through: Python, C++, Java, or some framework API.&lt;br&gt;
So our mental model becomes: “this is how concurrency works.”&lt;br&gt;
It’s not.&lt;br&gt;
It’s just how one runtime exposes it.&lt;/p&gt;
&lt;p&gt;That’s why things feel unpredictable:&lt;br&gt;
• “Why do I still get race conditions?”&lt;br&gt;
• “Why is my async code slower than sync?”&lt;br&gt;
• “Why doesn’t this scale across cores?”&lt;br&gt;
Because underneath:&lt;br&gt;
• the OS scheduler decides who runs&lt;br&gt;
• context switching is not free&lt;br&gt;
• cores share memory with non-uniform latency&lt;br&gt;
• caches can invalidate each other&lt;/p&gt;
&lt;p&gt;If you don’t understand that layer, you’re not really reasoning about concurrency.&lt;br&gt;
You’re just using it.&lt;/p&gt;
&lt;p&gt;To be clear — that’s fine at the beginning.&lt;br&gt;
But the moment you care about performance or correctness under load,&lt;br&gt;
you have to go down the stack.&lt;/p&gt;
&lt;p&gt;Hardware → OS → runtime → language.&lt;br&gt;
Not the other way around.&lt;/p&gt;
&lt;p&gt;Otherwise you’re not solving concurrency problems.&lt;br&gt;
You’re operating an API.&lt;/p&gt;
&lt;h3 id=&quot;abstractions-are-just-tools&quot;&gt;Abstractions are just tools&lt;/h3&gt;
&lt;p&gt;In previous chapters I talked about:&lt;br&gt;
• concurrency&lt;br&gt;
• memory&lt;br&gt;
• data types&lt;/p&gt;
&lt;p&gt;At first glance, these look like different topics.&lt;br&gt;
They’re not.&lt;/p&gt;
&lt;p&gt;They all share the same root problem:&lt;br&gt;
We learn abstractions… without understanding what they abstract.&lt;/p&gt;
&lt;p&gt;We learn:&lt;br&gt;
• async/await instead of how threads are scheduled&lt;br&gt;
• new/delete instead of how memory behaves&lt;br&gt;
• int/float instead of how data is represented&lt;/p&gt;
&lt;p&gt;So our mental model becomes: “this is how the system works”&lt;br&gt;
When in reality: “this is how one layer exposes it”&lt;/p&gt;
&lt;p&gt;And most of the time, that’s enough.&lt;br&gt;
Until it isn’t.&lt;/p&gt;
&lt;p&gt;The cracks appear when:&lt;br&gt;
• performance matters&lt;br&gt;
• systems scale&lt;br&gt;
• bugs become non-deterministic&lt;br&gt;
• behavior stops matching intuition&lt;/p&gt;
&lt;p&gt;That’s when abstraction stops helping and starts hiding the problem.&lt;/p&gt;
&lt;p&gt;This doesn’t mean everyone must become a hardware engineer.&lt;br&gt;
But it does mean: &lt;br&gt;
If you want to reason about systems, you need to understand the layers below them.&lt;/p&gt;
&lt;p&gt;A simple rule that helped me:&lt;br&gt;
When something feels “weird” or unpredictable — you’re probably missing a layer in your mental model.&lt;/p&gt;
&lt;p&gt;Go one level deeper:&lt;br&gt;
hardware → OS → runtime → language.&lt;br&gt;
That’s usually where the answer is.&lt;/p&gt;
&lt;p&gt;Abstractions are tools.&lt;br&gt;
But understanding is leverage.&lt;/p&gt;
&lt;h3 id=&quot;mental-model&quot;&gt;Mental model&lt;/h3&gt;
&lt;p&gt;A fair question may come up:&lt;br&gt;
“How do I actually learn all these lower layers without going down a 5-year rabbit hole?”&lt;/p&gt;
&lt;p&gt;Short answer:&lt;br&gt;
You don’t need to learn everything. You need to learn just enough to build a mental model.&lt;/p&gt;
&lt;p&gt;Here’s what actually works.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Learn one layer below your current work&lt;br&gt;
If you’re writing application code → learn runtime behavior&lt;br&gt;
If you’re using runtime features → learn OS basics&lt;br&gt;
If you’re already there → peek into hardware&lt;br&gt;
Don’t jump straight into CPU manuals. Go one step down.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Learn through problems, not theory&lt;br&gt;
Don’t start with textbooks.&lt;br&gt;
Start with questions like:&lt;br&gt;
• “Why is this slower than expected?”&lt;br&gt;
• “Why does this break under load?”&lt;br&gt;
• “Why does this behave differently on another machine?”&lt;br&gt;
Then dig until you hit the real cause. That’s where learning sticks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Build small, focused experiments&lt;br&gt;
You don’t need big projects.&lt;br&gt;
Try things like:&lt;br&gt;
• write a tiny thread pool&lt;br&gt;
• measure cache effects with different data layouts&lt;br&gt;
• intentionally create a race condition and observe it&lt;br&gt;
• compare contiguous vs scattered allocations&lt;br&gt;
You’ll learn more from one experiment than from hours of passive reading.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use tools that show reality&lt;br&gt;
Start simple:&lt;br&gt;
• profilers (CPU, memory)&lt;br&gt;
• timing measurements&lt;br&gt;
• system monitors&lt;br&gt;
Look at what the system is actually doing — not what you think it’s doing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Accept partial understanding&lt;br&gt;
You don’t need to master everything.&lt;br&gt;
If you walk away knowing:&lt;br&gt;
• caches exist and affect performance&lt;br&gt;
• threads are scheduled, not “run” by your code&lt;br&gt;
• memory is not uniform&lt;br&gt;
you’re already ahead of most developers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go deeper only when it pays off&lt;br&gt;
Not every problem needs low-level knowledge.&lt;br&gt;
But when something feels:&lt;br&gt;
• slow&lt;br&gt;
• unpredictable&lt;br&gt;
• “magical”&lt;br&gt;
that’s your signal to go down a layer.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You don’t need to become a systems expert.&lt;br&gt;
But you do need to know when the abstraction stops being enough.&lt;br&gt;
That’s the real skill.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26608/the-power-of-mental-models-for-developers</guid>
<pubDate>Wed, 09 Sep 2026 11:26:43 +0000</pubDate>
</item>
<item>
<title>The Microsoft Learn Roadmap I Would Give a First-Year CS Student</title>
<link>https://coderlegion.com/26511/the-microsoft-learn-roadmap-i-would-give-a-first-year-csstudent</link>
<description>&lt;p&gt;When I talk to first-year computer science students, I notice the same pattern almost every time.&lt;/p&gt;
&lt;p&gt;Someone has a playlist for Python.&lt;/p&gt;
&lt;p&gt;Someone else says you should learn C++ first.&lt;/p&gt;
&lt;p&gt;A YouTuber recommends learning cloud.&lt;/p&gt;
&lt;p&gt;Another says AI is the future.&lt;/p&gt;
&lt;p&gt;Then there are certifications, GitHub projects, roadmaps, coding platforms, hackathons, and dozens of tabs sitting open in the browser.&lt;br&gt;
The result?&lt;/p&gt;
&lt;p&gt;A lot of students spend more time planning what to learn than actually learning.&lt;/p&gt;
&lt;p&gt;I think this happens because we're exposed to an endless amount of technology content from day one. Every topic sounds important. Every roadmap looks urgent. Every certification seems like something we should start immediately.&lt;/p&gt;
&lt;p&gt;The mistake isn't curiosity.&lt;/p&gt;
&lt;p&gt;The mistake is trying to learn everything at once.&lt;br&gt;
Over time, I've become convinced that structured learning beats collecting tutorials.&lt;/p&gt;
&lt;p&gt;A hundred bookmarked videos don't automatically create progress. A clear sequence of skills usually does.&lt;/p&gt;
&lt;p&gt;If I were advising a first-year CS student today, this is the roadmap I would suggest. It's practical, realistic, and designed to fit alongside university studies instead of competing with them.&lt;/p&gt;
&lt;h3 id=&quot;stage-1-build-programming-fundamentals-first&quot;&gt;Stage 1: Build Programming Fundamentals First&lt;/h3&gt;
&lt;p&gt;This stage is not exciting.&lt;br&gt;
That's exactly why it's important.&lt;br&gt;
Before thinking about cloud platforms, machine learning models, cybersecurity tools, or DevOps pipelines, you need to become comfortable with basic programming concepts.&lt;br&gt;
Focus on things like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Variables&lt;/li&gt;
&lt;li&gt;Data types&lt;/li&gt;
&lt;li&gt;Conditions&lt;/li&gt;
&lt;li&gt;Loops&lt;/li&gt;
&lt;li&gt;Functions&lt;/li&gt;
&lt;li&gt;Arrays and data structures&lt;/li&gt;
&lt;li&gt;Problem solving&lt;/li&gt;
&lt;li&gt;Basic debugging&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The specific language matters less than most people think.&lt;/p&gt;
&lt;p&gt;Your university may start with C++, Java, Python, or C#. That's fine.&lt;/p&gt;
&lt;p&gt;What matters is understanding how programs work.&lt;/p&gt;
&lt;p&gt;I've seen students jump straight into trendy topics and then struggle because basic programming logic wasn't solid. When that foundation is weak, every advanced topic feels harder than it should.&lt;/p&gt;
&lt;p&gt;Microsoft Learn is useful here because many learning paths are structured, not random collections of videos.&lt;/p&gt;
&lt;p&gt;Instead of asking, &quot;What should I learn next?&quot;, the path already guides you through the next concept.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resource:&lt;/strong&gt;&lt;br&gt;
 &lt;a rel=&quot;nofollow&quot; href=&quot;https://learn.microsoft.com/?wt.mc_id=studentamb_496381&quot;&gt;https://learn.microsoft.com/?wt.mc_id=studentamb_496381&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At this stage, don't worry about building impressive projects.&lt;br&gt;
Learn how to write code that works.&lt;/p&gt;
&lt;h3 id=&quot;stage-2-learn-git-and-the-developer-workflow&quot;&gt;Stage 2: Learn Git and the Developer Workflow&lt;/h3&gt;
&lt;p&gt;This is one of the most overlooked skills among beginners.&lt;/p&gt;
&lt;p&gt;Many students spend months learning programming but still don't know how professional developers manage code.&lt;/p&gt;
&lt;p&gt;Learning Git early pays off much faster than most students expect.&lt;/p&gt;
&lt;p&gt;Get comfortable with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Repositories&lt;/li&gt;
&lt;li&gt;Commits&lt;/li&gt;
&lt;li&gt;Branches&lt;/li&gt;
&lt;li&gt;Pull requests&lt;/li&gt;
&lt;li&gt;Version control basics&lt;/li&gt;
&lt;li&gt;GitHub collaboration&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal isn't becoming a Git expert.&lt;/p&gt;
&lt;p&gt;The goal is making Git part of your normal workflow.&lt;/p&gt;
&lt;p&gt;Imagine you're working on an assignment.&lt;/p&gt;
&lt;p&gt;Instead of keeping files named:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;project_final
project_final2
project_final_actual
project_final_latest_final
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You use Git commits to track changes properly.&lt;br&gt;
That's a small shift, but it mirrors how software is built in real environments.&lt;/p&gt;
&lt;p&gt;The earlier you develop these habits, the easier team projects, hackathons, internships, and open-source contributions become.&lt;/p&gt;
&lt;h3 id=&quot;stage-3-choose-one-direction-instead-of-chasing-everything&quot;&gt;Stage 3: Choose One Direction Instead of Chasing Everything&lt;/h3&gt;
&lt;p&gt;This is where many students get stuck.&lt;/p&gt;
&lt;p&gt;They try to learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cloud&lt;/li&gt;
&lt;li&gt;AI&lt;/li&gt;
&lt;li&gt;Data Science&lt;/li&gt;
&lt;li&gt;Cybersecurity&lt;/li&gt;
&lt;li&gt;DevOps&lt;/li&gt;
&lt;li&gt;Mobile Development&lt;/li&gt;
&lt;li&gt;Web Development&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All at the same time.&lt;/p&gt;
&lt;p&gt;I wouldn't do that.&lt;/p&gt;
&lt;p&gt;After building programming fundamentals, pick one direction that genuinely interests you.&lt;br&gt;
Not forever.&lt;br&gt;
Just for the next few months.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;h4 id=&quot;cloud&quot;&gt;Cloud&lt;/h4&gt;
&lt;p&gt;Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cloud fundamentals&lt;/li&gt;
&lt;li&gt;Virtual machines&lt;/li&gt;
&lt;li&gt;Storage&lt;/li&gt;
&lt;li&gt;Networking concepts&lt;/li&gt;
&lt;li&gt;Basic Azure services&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;ai&quot;&gt;AI&lt;/h4&gt;
&lt;p&gt;Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Data handling&lt;/li&gt;
&lt;li&gt;Machine learning fundamentals&lt;/li&gt;
&lt;li&gt;Generative AI concepts&lt;/li&gt;
&lt;li&gt;Model evaluation basics&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;data&quot;&gt;Data&lt;/h4&gt;
&lt;p&gt;Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL&lt;/li&gt;
&lt;li&gt;Data visualization&lt;/li&gt;
&lt;li&gt;Data analysis&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;devops&quot;&gt;DevOps&lt;/h4&gt;
&lt;p&gt;Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux basics&lt;/li&gt;
&lt;li&gt;CI/CD concepts&lt;/li&gt;
&lt;li&gt;Containers&lt;/li&gt;
&lt;li&gt;Automation&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;security&quot;&gt;Security&lt;/h4&gt;
&lt;p&gt;Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Security fundamentals&lt;/li&gt;
&lt;li&gt;Identity concepts&lt;/li&gt;
&lt;li&gt;Threat awareness&lt;/li&gt;
&lt;li&gt;Security best practices&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;application-development&quot;&gt;Application Development&lt;/h4&gt;
&lt;p&gt;Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frontend frameworks&lt;/li&gt;
&lt;li&gt;Backend basics&lt;/li&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;Databases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal isn't specialization.&lt;/p&gt;
&lt;p&gt;The goal is reducing noise.&lt;/p&gt;
&lt;p&gt;Students often think choosing one path means missing out on everything else.&lt;/p&gt;
&lt;p&gt;Actually, it usually helps you learn faster because your attention isn't split across six different roadmaps.&lt;/p&gt;
&lt;p&gt;Microsoft Learn has dedicated learning paths for all of these areas, making exploration much easier without constantly searching for the next resource.&lt;/p&gt;
&lt;h3 id=&quot;stage-4-build-a-small-project&quot;&gt;Stage 4: Build a Small Project&lt;/h3&gt;
&lt;p&gt;This is the stage that separates learning from consuming content.&lt;/p&gt;
&lt;p&gt;At some point, tutorials stop helping.&lt;/p&gt;
&lt;p&gt;The only way forward is building something.&lt;/p&gt;
&lt;p&gt;The project does not need to be impressive.&lt;/p&gt;
&lt;p&gt;In fact, I would intentionally keep it small.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A student expense tracker&lt;/li&gt;
&lt;li&gt;A task management application&lt;/li&gt;
&lt;li&gt;A weather dashboard&lt;/li&gt;
&lt;li&gt;A simple chatbot&lt;/li&gt;
&lt;li&gt;A portfolio website&lt;/li&gt;
&lt;li&gt;A basic inventory system&lt;/li&gt;
&lt;li&gt;A data analysis dashboard&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The purpose of the project isn't showing off.&lt;/p&gt;
&lt;p&gt;The purpose is discovering what you don't know yet.&lt;/p&gt;
&lt;p&gt;Projects expose gaps that tutorials hide.&lt;/p&gt;
&lt;p&gt;You suddenly realize:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Your Git workflow needs improvement.&lt;/li&gt;
&lt;li&gt;Your debugging skills need work.&lt;/li&gt;
&lt;li&gt;Your database knowledge is incomplete.&lt;/li&gt;
&lt;li&gt;Your API integration doesn't work properly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's normal.&lt;/p&gt;
&lt;p&gt;Those lessons are valuable because they're connected to real problems.&lt;/p&gt;
&lt;p&gt;University courses often provide theoretical foundations. Personal projects provide practical context.&lt;/p&gt;
&lt;p&gt;The combination of both is powerful.&lt;/p&gt;
&lt;h3 id=&quot;stage-5-validate-practical-skills&quot;&gt;Stage 5: Validate Practical Skills&lt;/h3&gt;
&lt;p&gt;A lot of students ask me when they should think about certifications and credentials.&lt;/p&gt;
&lt;p&gt;My answer is usually:&lt;/p&gt;
&lt;p&gt;Not first.&lt;/p&gt;
&lt;p&gt;Not second.&lt;/p&gt;
&lt;p&gt;Definitely not before learning the fundamentals.&lt;/p&gt;
&lt;p&gt;Once you've built some practical skills and completed projects, then credentials start making more sense.&lt;/p&gt;
&lt;p&gt;One thing I like about Microsoft Learn is that it doesn't only focus on reading content.&lt;/p&gt;
&lt;p&gt;Students can also explore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Role-based learning paths&lt;/li&gt;
&lt;li&gt;Applied Skills&lt;/li&gt;
&lt;li&gt;Technical credentials&lt;/li&gt;
&lt;li&gt;Hands-on exercises&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Applied Skills are particularly interesting because they focus on demonstrating practical abilities in specific scenarios rather than simply collecting study material.&lt;br&gt;
At this point, credentials become evidence of learning rather than substitutes for learning.&lt;br&gt;
That's an important difference.&lt;/p&gt;
&lt;h3 id=&quot;how-i-would-use-microsoft-learn-alongside-university&quot;&gt;How I Would Use Microsoft Learn Alongside University&lt;/h3&gt;
&lt;p&gt;I would not replace university coursework with online learning.&lt;/p&gt;
&lt;p&gt;I would combine them.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;p&gt;If you're studying databases this semester, reinforce those concepts with database-related learning paths.&lt;/p&gt;
&lt;p&gt;If you're learning networking concepts in class, explore cloud networking fundamentals afterward.&lt;/p&gt;
&lt;p&gt;If you're working on a software engineering project, use GitHub and developer workflow resources to support that work.&lt;/p&gt;
&lt;p&gt;The best approach isn't separating university learning from industry learning.&lt;/p&gt;
&lt;p&gt;It's connecting them.&lt;/p&gt;
&lt;p&gt;When both reinforce each other, concepts tend to stick much better.&lt;/p&gt;
&lt;h3 id=&quot;what-i-would-not-do&quot;&gt;What I Would NOT Do&lt;/h3&gt;
&lt;p&gt;If I were starting again as a first-year CS student, here are several things I would actively avoid.&lt;/p&gt;
&lt;h3 id=&quot;i-would-not-collect-endless-roadmaps-&quot;&gt;I would not collect endless roadmaps.&lt;/h3&gt;
&lt;p&gt;Eventually, you need one roadmap and consistent effort.&lt;/p&gt;
&lt;h3 id=&quot;i-would-not-start-five-certifications-at-the-same-time-&quot;&gt;I would not start five certifications at the same time.&lt;/h3&gt;
&lt;p&gt;Finishing one meaningful learning path is usually better than partially completing several.&lt;/p&gt;
&lt;h3 id=&quot;i-would-not-chase-every-technology-trend-&quot;&gt;I would not chase every technology trend.&lt;/h3&gt;
&lt;p&gt;New tools appear constantly.&lt;/p&gt;
&lt;p&gt;Foundational skills remain valuable much longer.&lt;/p&gt;
&lt;h3 id=&quot;i-would-not-spend-months-only-watching-tutorials-&quot;&gt;I would not spend months only watching tutorials.&lt;/h3&gt;
&lt;p&gt;Learning requires building.&lt;/p&gt;
&lt;p&gt;Projects expose weaknesses that passive learning cannot.&lt;/p&gt;
&lt;h3 id=&quot;i-would-not-compare-my-progress-to-students-posting-achievements-online-&quot;&gt;I would not compare my progress to students posting achievements online.&lt;/h3&gt;
&lt;p&gt;Everyone's learning journey is different.&lt;/p&gt;
&lt;p&gt;Comparisons often create pressure without creating progress.&lt;/p&gt;
&lt;h3 id=&quot;a-practical-weekly-routine&quot;&gt;A Practical Weekly Routine&lt;/h3&gt;
&lt;p&gt;If you're currently in your first year, I would keep things simple.&lt;/p&gt;
&lt;h4 id=&quot;monday-to-friday&quot;&gt;Monday to Friday&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Focus primarily on university coursework.&lt;/li&gt;
&lt;li&gt;Spend 30 to 45 minutes on a Microsoft Learn learning path.&lt;/li&gt;
&lt;li&gt;Take notes on concepts that connect to your classes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;saturday&quot;&gt;Saturday&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Work on a personal project for 2 to 3 hours.&lt;/li&gt;
&lt;li&gt;Apply something learned during the week&lt;br&gt;
.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;sunday&quot;&gt;Sunday&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Review notes.&lt;/li&gt;
&lt;li&gt;Update project progress.&lt;/li&gt;
&lt;li&gt;Plan the next week's learning goals.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's it.&lt;/p&gt;
&lt;p&gt;Not six hours every day.&lt;/p&gt;
&lt;p&gt;Not three different certifications simultaneously.&lt;/p&gt;
&lt;p&gt;Not twenty browser tabs full of unfinished tutorials.&lt;/p&gt;
&lt;p&gt;Just consistent progress.&lt;/p&gt;
&lt;p&gt;The biggest lesson I've learned is that successful students aren't necessarily the ones learning the most technologies. They're often the ones following a structured path long enough to build real skills.&lt;/p&gt;
&lt;p&gt;If you're feeling overwhelmed by the number of technologies out there, don't try to learn everything this semester.&lt;/p&gt;
&lt;p&gt;Pick a foundation. Choose a direction. Build something small. Stay consistent.&lt;/p&gt;
&lt;p&gt;And if you're looking for a structured place to explore technical topics, Microsoft Learn is a resource worth keeping in your toolkit:&lt;/p&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://learn.microsoft.com/?wt.mc_id=studentamb_496381&quot;&gt;https://learn.microsoft.com/?wt.mc_id=studentamb_496381&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you're currently a CS student, what's the one technology area you're focusing on this semester, and why?&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26511/the-microsoft-learn-roadmap-i-would-give-a-first-year-csstudent</guid>
<pubDate>Tue, 08 Sep 2026 16:37:09 +0000</pubDate>
</item>
<item>
<title>From Isolation to AURA: Finding Safe Developer Spaces</title>
<link>https://coderlegion.com/26395/from-isolation-to-aura-finding-safe-developer-spaces</link>
<description>&lt;p&gt;When I transitioned into software development through online courses, I was operating in a complete vacuum. With no tech professionals in my immediate circle, every obstacle felt magnified. In this article, I reflect on how different platform dynamics — from personal branding showcases to competitive sandboxes — directly impacted my growth as an engineer, and how finding the right low-noise space shifted my perspective.&lt;/p&gt;
&lt;p&gt;Reading technical documentation or complex articles back then often pushed me into deeper isolation rather than helping. Looking back now, I rationally understand that &lt;strong&gt;&quot;there are no dumb questions&quot;&lt;/strong&gt;. But when you are stuck in a vacuum, without peers who share the same path, those doubts don't feel like logical problems — they feel like overwhelming personal failures. I was terrified to ask my course mentors basic questions, relying almost entirely on AI to navigate forward by trial and error.&lt;/p&gt;
&lt;h3 id=&quot;the-linkedin-showcase-amp-the-learning-curve&quot;&gt;The LinkedIn Showcase &amp;amp; The Learning Curve&lt;/h3&gt;
&lt;p&gt;When I joined &lt;strong&gt;LinkedIn&lt;/strong&gt;, reading the feed was intimidating. Everyone appeared wildly successful, with complex titles and flawless career trajectories.&lt;/p&gt;
&lt;p&gt;Of course, this is my purely personal perspective — perhaps due to my relative newcomer status in tech, I simply haven't fully mastered the rules of this game yet. In reality, behind the polished storefront, there are undoubtedly many genuine success stories. But in a space heavily driven by personal branding and character limits, I felt the constant pressure of a &quot;numbers game.&quot; Fortunately, even within that high-noise environment, I managed to separate the wheat from the chaff and connected with a few incredible people who took the time to review my code and collaborate.&lt;/p&gt;
&lt;h3 id=&quot;dev-to-the-competitive-sandbox&quot;&gt;DEV.to: The Competitive Sandbox&lt;/h3&gt;
&lt;p&gt;Later, I explored &lt;strong&gt;DEV.to.&lt;/strong&gt; I genuinely respect DEV.to for its platform structure — especially the hackathons and writing contests. It's a fantastic space where developers can test their skills, build projects for themselves, win real prizes, and get discovered.&lt;/p&gt;
&lt;p&gt;However, while DEV.to offered the excitement of competition, the lack of direct messaging meant that meaningful, two-way technical discussions often got lost under algorithm-driven feeds and high-volume tutorials.&lt;/p&gt;
&lt;h3 id=&quot;coderlegion-finding-a-low-noise-dynamic&quot;&gt;CoderLegion: Finding a Low-Noise Dynamic&lt;/h3&gt;
&lt;p&gt;For my current workflow, I realized I needed a hybrid dynamic: the ability to publish deep technical breakdowns alongside a low-noise, direct way to debate architecture with peers without formal barriers.&lt;/p&gt;
&lt;p&gt;This isn't to say one platform is universally &quot;better&quot; than another, but this quieter, human-centric approach (which I found in spaces like &lt;strong&gt;CoderLegion&lt;/strong&gt;) simply fit my personal learning and engineering style much better. Stepping out of the &quot;silent observer&quot; role in a low-noise space gave me the confidence to refine my ideas. It was through these direct, unhurried discussions that the concept behind my &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/kate8382/error-logger-viewer.git&quot;&gt;AURA (AI User Risk Assessment)&lt;/a&gt;&lt;/strong&gt; framework transformed from a rough hypothesis into a structured, production-ready specification.&lt;/p&gt;
&lt;h3 id=&quot;why-safe-spaces-matter-a-lesson-from-banking&quot;&gt;Why Safe Spaces Matter: A Lesson from Banking&lt;/h3&gt;
&lt;p&gt;This realization didn't come out of nowhere. Before software engineering, I worked at ProCredit Bank — one of the most successful financial institutions in Moldova, where many alumni now hold leading positions across the country's financial sector.&lt;/p&gt;
&lt;p&gt;One practice stuck with me: our unfiltered brainstorming sessions (or what we informally called &quot;throwing ideas at the fan&quot;). We had dedicated spaces where anyone could voice any raw, unrefined, or seemingly &quot;silly&quot; idea without fear of judgment. Because the fear of looking incompetent was removed, those sessions consistently yielded our strongest architectural decisions.&lt;/p&gt;
&lt;p&gt;Software engineering requires that same psychological safety. Great code isn't written in fear; it emerges through open dialogue.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;There is no single &quot;right&quot; platform for everyone. Some thrive in the fast-paced visibility of &lt;strong&gt;LinkedIn&lt;/strong&gt;; others find their rhythm in &lt;strong&gt;DEV.to&lt;/strong&gt; hackathons.&lt;/p&gt;
&lt;p&gt;My advice to fellow developers — especially those feeling isolated — is simply to find the space where you don't have to focus so much on numbers, and where you feel safe enough to ask questions, debate architecture, and turn raw ideas into real engineering. For me, that space became &lt;strong&gt;right here on CoderLegion&lt;/strong&gt; (so if you're reading this on the platform, consider yourself in good company!))), and it made all the difference in my journey.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;💬 Where have you found your safe space for deep engineering discussions without the noise? I’d love to hear your thoughts and experience in the comments!&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26395/from-isolation-to-aura-finding-safe-developer-spaces</guid>
<pubDate>Mon, 07 Sep 2026 12:00:45 +0000</pubDate>
</item>
<item>
<title>Learn Number of Islands, Invert Binary Tree, and Course Schedule with Step-by-Step</title>
<link>https://coderlegion.com/26218/learn-number-of-islands-invert-binary-tree-and-course-schedule-with-step-by-step</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer who is surprised by how cold it is in the Netherlands even though it’s still summer. 😸&lt;/p&gt;
&lt;p&gt;Have you used &lt;strong&gt;DSA View View&lt;/strong&gt; already? 👀👀&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&quot;&gt;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;DSA View View allows you to understand DSA by visualizing how your implementation actually runs.&lt;/p&gt;
&lt;p&gt;In the previous articles, we looked at problems like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two Sum&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Bubble Sort&lt;/li&gt;
&lt;li&gt;Valid Parentheses&lt;/li&gt;
&lt;li&gt;Reverse Linked List&lt;/li&gt;
&lt;li&gt;Maximum Depth of Binary Tree&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort-374o&quot;&gt;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort-374o&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/learn-valid-parentheses-reverse-linked-list-and-tree-max-depth-with-step-by-step-visualization-in-3o09&quot;&gt;https://dev.to/nyaomaru/learn-valid-parentheses-reverse-linked-list-and-tree-max-depth-with-step-by-step-visualization-in-3o09&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This time, let's try three more classic problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Number of Islands&lt;/li&gt;
&lt;li&gt;Invert Binary Tree&lt;/li&gt;
&lt;li&gt;Course Schedule&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These three problems introduce some very useful ways of thinking:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Explore connected things
Transform a tree with recursion
Resolve dependencies in the right order
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementations are not huge.&lt;/p&gt;
&lt;p&gt;But the runtime can become surprisingly difficult to hold in our heads.&lt;/p&gt;
&lt;p&gt;So let's view what actually happens. 👀&lt;/p&gt;
&lt;p&gt;Let's learn together! 😸&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/618ol6g3ivydm71t0r5o.png&quot; alt=&quot;Step view 3&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-number-of-islands&quot;&gt;🏝️ Number of Islands&lt;/h3&gt;
&lt;p&gt;Let's start with &lt;strong&gt;Number of Islands&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have this grid:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1 0 0
1 0 0 1
0 0 1 1
0 0 0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;1&lt;/code&gt; means land. 🏝️&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0&lt;/code&gt; means water. 🌊&lt;/p&gt;
&lt;p&gt;Land connected vertically or horizontally belongs to the same island.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;So how many islands are there?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's look at the first group.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1
1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These cells are connected.&lt;/p&gt;
&lt;p&gt;So they form &lt;strong&gt;one island&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On the right side&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
  1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Those cells are also connected.&lt;/p&gt;
&lt;p&gt;So the answer is &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Nice! 🏝️🏝️&lt;/p&gt;
&lt;p&gt;But how do we make the code understand that several &lt;code&gt;1&lt;/code&gt;s belong to the same island?&lt;/p&gt;
&lt;h4 id=&quot;find-one-land-then-explore-all-connected-land&quot;&gt;Find One Land, Then Explore All Connected Land&lt;/h4&gt;
&lt;p&gt;The basic idea is&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;When we find a new &lt;code&gt;1&lt;/code&gt;, count one island and visit all land connected to it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's use this implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function numIslands(grid: string[][]): number {
  let islands = 0;

  const visit = (row: number, col: number): void =&amp;gt; {
    if (row &amp;lt; 0 || col &amp;lt; 0) return;
    if (row &amp;gt;= grid.length || col &amp;gt;= grid[row].length) return;
    if (grid[row][col] !== &quot;1&quot;) return;

    grid[row][col] = &quot;0&quot;;
    visit(row + 1, col);
    visit(row - 1, col);
    visit(row, col + 1);
    visit(row, col - 1);
  };

  for (let row = 0; row &amp;lt; grid.length; row++) {
    for (let col = 0; col &amp;lt; grid[row].length; col++) {
      if (grid[row][col] === &quot;1&quot;) {
        islands++;
        visit(row, col);
      }
    }
  }

  return islands;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two important parts.&lt;/p&gt;
&lt;p&gt;First, we scan the grid&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (let row = 0; row &amp;lt; grid.length; row++) {
  for (let col = 0; col &amp;lt; grid[row].length; col++) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, when we find land&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (grid[row][col] === &quot;1&quot;) {
  islands++;
  visit(row, col);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We count a new island.&lt;/p&gt;
&lt;p&gt;But then &lt;code&gt;visit()&lt;/code&gt; does something important.&lt;/p&gt;
&lt;p&gt;It removes all land connected to that island from future consideration.&lt;/p&gt;
&lt;h4 id=&quot;why-do-we-change-1-to-0-&quot;&gt;Why Do We Change &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;Inside &lt;code&gt;visit()&lt;/code&gt; we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;grid[row][col] = &quot;0&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first, changing land into water looks a little strange. 😿&lt;/p&gt;
&lt;p&gt;But here, &lt;code&gt;0&lt;/code&gt; really means&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;We already visited this land.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's follow a tiny example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1
1 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start at the top-left. And we found land! So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;islands = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;visit(0, 0);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside &lt;code&gt;visit()&lt;/code&gt;, we mark it as visited.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 1
1 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we visit the four directions&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;down
up
right
left
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Going down finds another &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 1
1 0
↑
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we visit it too.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 1
0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Going right from the original cell also finds land.&lt;/p&gt;
&lt;p&gt;Visit it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 0
0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the whole connected island has disappeared from our search.&lt;/p&gt;
&lt;p&gt;When the outer loops continue, there is no &lt;code&gt;1&lt;/code&gt; left in that island to count again.&lt;/p&gt;
&lt;p&gt;That's the key idea.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Count once, then mark the whole connected area as visited.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;why-four-recursive-calls-&quot;&gt;Why Four Recursive Calls?&lt;/h4&gt;
&lt;p&gt;We use&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;visit(row + 1, col);
visit(row - 1, col);
visit(row, col + 1);
visit(row, col - 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;        up
         ↑
left ← current → right
         ↓
        down
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each visited cell asks&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Is there more land next to me?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And every newly discovered land cell asks the same question again.&lt;/p&gt;
&lt;p&gt;This continues until we reach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;water&lt;/li&gt;
&lt;li&gt;outside the grid&lt;/li&gt;
&lt;li&gt;land we already visited&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those cases stop the recursion.&lt;/p&gt;
&lt;h4 id=&quot;the-base-cases&quot;&gt;The Base Cases&lt;/h4&gt;
&lt;p&gt;These lines protect us&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (row &amp;lt; 0 || col &amp;lt; 0) return;
if (row &amp;gt;= grid.length || col &amp;gt;= grid[row].length) return;
if (grid[row][col] !== &quot;1&quot;) return;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If we walk outside the grid, stop.&lt;/li&gt;
&lt;li&gt;If we reach water, stop.&lt;/li&gt;
&lt;li&gt;If we reach a cell we already changed to &lt;code&gt;0&lt;/code&gt;, stop.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Otherwise, continue exploring.&lt;/p&gt;
&lt;h4 id=&quot;let-s-follow-two-islands&quot;&gt;Let's Follow Two Islands&lt;/h4&gt;
&lt;p&gt;Consider&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1 0
0 0 1
0 1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scan begins at the top-left.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 1 0
↑
0 0 1
0 1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Found land.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;islands = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;visit()&lt;/code&gt; removes everything connected to it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 0 0
0 0 1
0 1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The loops continue.&lt;/p&gt;
&lt;p&gt;Eventually we reach&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 0 0
0 0 1
    ↑
0 1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;islands = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;visit()&lt;/code&gt; explores that entire connected area.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 0 0
0 0 0
0 0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2 islands
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🎉&lt;/p&gt;
&lt;h4 id=&quot;complexity&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;Every cell is processed at most a small number of times.&lt;/p&gt;
&lt;p&gt;If the grid has &lt;code&gt;m&lt;/code&gt; rows and &lt;code&gt;n&lt;/code&gt; columns&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(m × n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the worst case, the recursive call stack may grow with the number of land cells.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Space: O(m × n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is one of those algorithms where the final code is pretty small.&lt;/p&gt;
&lt;p&gt;But while reading it, there are a lot of things moving at once&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;row
col
grid
islands
recursive calls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then suddenly we see&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;grid[row][col] = &quot;0&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Why did that cell disappear?&lt;/li&gt;
&lt;li&gt;Which recursive call are we inside?&lt;/li&gt;
&lt;li&gt;Which cells belong to the current island?&lt;/li&gt;
&lt;li&gt;Where will the outer loop continue after recursion finishes?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is a lot to simulate mentally. 😿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/c2q4mnfvnw0quklb52r9.gif&quot; alt=&quot;Number Of Islands&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoibnVtYmVyLW9mLWlzbGFuZHMiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoibnVtYmVyLW9mLWlzbGFuZHMiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we view it step by step, the idea becomes much more visual.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Find land
  ↓
islands++
  ↓
visit connected land
  ↓
mark it visited
  ↓
expand up / down / left / right
  ↓
return to scanning
  ↓
find next island
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of thinking about recursion first, I like to think about it like this&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Find one island, paint the whole island away, then keep searching.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;🏝️😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-invert-binary-tree&quot;&gt;🌳 Invert Binary Tree&lt;/h3&gt;
&lt;p&gt;Next, let's invert a binary tree.&lt;/p&gt;
&lt;p&gt;Suppose we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;        4
       / \
      2   7
     / \ / \
    1  3 6  9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want to change&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;        4
       / \
      7   2
     / \ / \
    9  6 3  1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every left child becomes the right child.&lt;/p&gt;
&lt;p&gt;Every right child becomes the left child.&lt;/p&gt;
&lt;p&gt;Simple, right?&lt;/p&gt;
&lt;p&gt;Well, the final implementation is also surprisingly small.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function invertTree(root: TreeNode | null): TreeNode | null {
  if (root === null) return null;

  const left = invertTree(root.left);
  const right = invertTree(root.right);

  root.left = right;
  root.right = left;

  return root;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's almost suspiciously short. 👀&lt;/p&gt;
&lt;h4 id=&quot;go-down-first&quot;&gt;Go Down First&lt;/h4&gt;
&lt;p&gt;Let's use a smaller tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
   / \
  2   3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start at node &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But we don't swap immediately.&lt;/p&gt;
&lt;p&gt;First,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const left = invertTree(root.left);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we go to node &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Node &lt;code&gt;2&lt;/code&gt; also tries to invert its left child.&lt;/p&gt;
&lt;p&gt;But there is no child.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (root === null) return null;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;returns &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The same thing happens on the right side of node &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now node &lt;code&gt;2&lt;/code&gt; has&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = null
right = null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;root.left = right;
root.right = left;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;does not visibly change anything.&lt;/p&gt;
&lt;p&gt;Node &lt;code&gt;2&lt;/code&gt; returns.&lt;/p&gt;
&lt;p&gt;Then node &lt;code&gt;1&lt;/code&gt; explores its right subtree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node &lt;code&gt;3&lt;/code&gt; also has no children, so it returns after the same process.&lt;/p&gt;
&lt;p&gt;Only then do we come back to node &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = 2
right = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we do&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;root.left = right;
root.right = left;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
   / \
  2   3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;becomes 👇&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
   / \
  3   2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done! 🎉&lt;/p&gt;
&lt;h4 id=&quot;the-important-part-the-swap-happens-on-the-way-back&quot;&gt;The Important Part: The Swap Happens on the Way Back&lt;/h4&gt;
&lt;p&gt;This is what makes the recursive solution interesting.&lt;/p&gt;
&lt;p&gt;The function first goes down.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
↓
2
↓
null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then it comes back.&lt;/p&gt;
&lt;p&gt;Later it explores the other side.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
↓
3
↓
null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And after both children have returned, the current node swaps them.&lt;/p&gt;
&lt;p&gt;So the flow is more like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Go left
  ↓
Invert left subtree
  ↓
Go right
  ↓
Invert right subtree
  ↓
Swap the returned subtrees
  ↓
Return current node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The tree transformation is built while recursion unwinds.&lt;/p&gt;
&lt;h4 id=&quot;a-slightly-bigger-example&quot;&gt;A Slightly Bigger Example&lt;/h4&gt;
&lt;p&gt;Let's look at&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;      4
     / \
    2   7
   / \
  1   3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start at &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;invertTree(4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;invertTree(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;invertTree(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node &lt;code&gt;1&lt;/code&gt; returns.&lt;/p&gt;
&lt;p&gt;Then node &lt;code&gt;3&lt;/code&gt; returns.&lt;/p&gt;
&lt;p&gt;Now node &lt;code&gt;2&lt;/code&gt; has&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = 1
right = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swap them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    2
   / \
  3   1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then recursion returns to &lt;code&gt;4&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The right subtree rooted at &lt;code&gt;7&lt;/code&gt; is also processed.&lt;/p&gt;
&lt;p&gt;Finally node &lt;code&gt;4&lt;/code&gt; receives&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = inverted subtree rooted at 2
right = inverted subtree rooted at 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and swaps them.&lt;/p&gt;
&lt;p&gt;The final tree becomes&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;      4
     / \
    7   2
       / \
      3   1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The interesting thing is that each node only needs to know about its own two children.&lt;/p&gt;
&lt;p&gt;It doesn't need to understand the whole tree.&lt;/p&gt;
&lt;h4 id=&quot;complexity-1&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;We visit every node once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time: O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The recursive call stack depends on the height of the tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Space: O(h)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a balanced tree&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;O(log n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the worst case&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-1&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is exactly where recursion can become difficult to mentally simulate.&lt;/p&gt;
&lt;p&gt;The code says&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const left = invertTree(root.left);
const right = invertTree(root.right);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;root.left = right;
root.right = left;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But my brain immediately starts asking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which root are we talking about now?&lt;/li&gt;
&lt;li&gt;Did node 2 already swap?&lt;/li&gt;
&lt;li&gt;Are we still going down?&lt;/li&gt;
&lt;li&gt;Or are we coming back up?&lt;/li&gt;
&lt;li&gt;What does left contain at this moment?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;😿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/bvxlf930nyny6tzvpcbd.gif&quot; alt=&quot;Invert Tree&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiaW52ZXJ0LWJpbmFyeS10cmVlIiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiaW52ZXJ0LWJpbmFyeS10cmVlIiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we step through the runtime, we can separate two different movements.&lt;/p&gt;
&lt;p&gt;The final code is short.&lt;/p&gt;
&lt;p&gt;But the actual runtime has a rhythm 🎵&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Go down
↓
Return
↓
Swap
↓
Return
↓
Swap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once I can see that rhythm, the recursive solution feels much less magical. 🌳😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-course-schedule&quot;&gt;🎓 Course Schedule&lt;/h3&gt;
&lt;p&gt;Finally, let's look at &lt;strong&gt;Course Schedule&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This one is a little more difficult.&lt;/p&gt;
&lt;p&gt;Suppose we have three courses&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0
1
2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the prerequisites are&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 0]
[2, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;To take course 1, finish course 0 first.
To take course 2, finish course 1 first.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the dependency looks like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Can we finish all courses?&lt;/p&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;p&gt;We can take them in this order &lt;code&gt;0 → 1 → 2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Easy.&lt;/p&gt;
&lt;p&gt;But what if the dependencies look like this?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1
↑   ↓
└── 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 needs 2
1 needs 0
2 needs 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everyone is waiting for someone else.&lt;/p&gt;
&lt;p&gt;We can never start.&lt;/p&gt;
&lt;p&gt;That is a &lt;strong&gt;cycle&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And if there is a cycle, we cannot finish all courses.&lt;/p&gt;
&lt;h4 id=&quot;build-the-graph&quot;&gt;Build the Graph&lt;/h4&gt;
&lt;p&gt;Here is the implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function canFinish(numCourses: number, prerequisites: number[][]): boolean {
  const graph: number[][] = Array.from({ length: numCourses }, () =&amp;gt; []);
  const indegree: number[] = Array(numCourses).fill(0);

  for (const [course, prerequisite] of prerequisites) {
    graph[prerequisite].push(course);
    indegree[course]++;
  }

  const queue: number[] = [];
  for (let course = 0; course &amp;lt; numCourses; course++) {
    if (indegree[course] === 0) queue.push(course);
  }

  let completed = 0;
  for (let head = 0; head &amp;lt; queue.length; head++) {
    const course = queue[head];
    completed++;

    for (const next of graph[course]) {
      indegree[next]--;
      if (indegree[next] === 0) queue.push(next);
    }
  }

  return completed === numCourses;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a few moving parts here.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;graph
indegree
queue
completed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is exactly the kind of algorithm where every individual line makes sense.&lt;/p&gt;
&lt;p&gt;but the whole thing can still feel confusing. 😿&lt;/p&gt;
&lt;p&gt;Let's break it down.&lt;/p&gt;
&lt;h4 id=&quot;what-is-graph-&quot;&gt;What Is &lt;code&gt;graph&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;For&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we want to know&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;After I finish this course, which courses become closer to being available?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;graph[0] = [1]
graph[1] = [2]
graph[2] = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That means&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Finish 0
↓
course 1 is affected

Finish 1
↓
course 2 is affected
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We build that here&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;graph[prerequisite].push(course);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;what-is-indegree-&quot;&gt;What Is &lt;code&gt;indegree&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;indegree&lt;/code&gt; tells us how many prerequisites a course is still waiting for.&lt;/p&gt;
&lt;p&gt;For&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;course 0: 0 prerequisites
course 1: 1 prerequisite
course 2: 1 prerequisite
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree = [0, 1, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;0&lt;/code&gt; is special. Because it does not need anything before it.&lt;/p&gt;
&lt;p&gt;So we can start there immediately.&lt;/p&gt;
&lt;h4 id=&quot;start-with-courses-that-need-nothing&quot;&gt;Start With Courses That Need Nothing&lt;/h4&gt;
&lt;p&gt;We build the queue&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (let course = 0; course &amp;lt; numCourses; course++) {
  if (indegree[course] === 0) queue.push(course);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For our example&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree = [0, 1, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only course &lt;code&gt;0&lt;/code&gt; has zero prerequisites.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Course 0 is currently available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;finish-course-0&quot;&gt;Finish Course 0&lt;/h4&gt;
&lt;p&gt;Take&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;course = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;completed++;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now look at courses depending on &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;graph[0] = [1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;1&lt;/code&gt; was waiting for one prerequisite.&lt;/p&gt;
&lt;p&gt;But course &lt;code&gt;0&lt;/code&gt; is now complete.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;indegree[1]--;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[1] = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now course &lt;code&gt;1&lt;/code&gt; needs nothing.&lt;/p&gt;
&lt;p&gt;So we add it to the queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;finish-course-1&quot;&gt;Finish Course 1&lt;/h4&gt;
&lt;p&gt;Next&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;course = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;2&lt;/code&gt; depends on &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[2]: 1 → 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add it to the queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;finish-course-2&quot;&gt;Finish Course 2&lt;/h4&gt;
&lt;p&gt;Finally&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;course = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;numCourses = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;completed === numCourses; // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can finish everything! 🎉&lt;/p&gt;
&lt;h4 id=&quot;why-does-this-detect-a-cycle-&quot;&gt;Why Does This Detect a Cycle?&lt;/h4&gt;
&lt;p&gt;Now let's try&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1
↑   ↓
└── 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every course has one prerequisite.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree = [1, 1, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We try to build the initial queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (indegree[course] === 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But there is no course with indegree &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, nothing can start.&lt;/p&gt;
&lt;p&gt;Therefore&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed = 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 === 3 // false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We cannot finish the courses.&lt;/p&gt;
&lt;h4 id=&quot;another-example&quot;&gt;Another Example&lt;/h4&gt;
&lt;p&gt;Suppose&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 2
1 → 2
2 → 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;2&lt;/code&gt; needs both &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree = [0, 0, 2, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The initial queue is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finish &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[2]: 2 → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Course &lt;code&gt;2&lt;/code&gt; is still waiting.&lt;/p&gt;
&lt;p&gt;So don't add it yet.&lt;/p&gt;
&lt;p&gt;Finish &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[2]: 1 → 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now course &lt;code&gt;2&lt;/code&gt; is ready.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finish &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree[3]: 1 → 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0, 1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything can be completed.&lt;/p&gt;
&lt;p&gt;This is the key idea&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;When all prerequisites for a course are resolved, that course becomes available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;why-use-head-instead-of-shift-&quot;&gt;Why Use &lt;code&gt;head&lt;/code&gt; Instead of &lt;code&gt;shift()&lt;/code&gt;?&lt;/h4&gt;
&lt;p&gt;The queue is processed like this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;for (let head = 0; head &amp;lt; queue.length; head++) {
  const course = queue[head]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of repeatedly doing&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;queue.shift();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we keep an index pointing to the next item to process.&lt;/p&gt;
&lt;p&gt;So the queue can grow while we iterate through it.&lt;/p&gt;
&lt;p&gt;For example&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;queue = [0]

process 0
↓
queue = [0, 1]

process 1
↓
queue = [0, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;head&lt;/code&gt; simply moves forward.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
↑
head
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
    ↑
   head
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2
        ↑
       head
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;complexity-2&quot;&gt;Complexity&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;V = number of courses
E = number of prerequisite relationships
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We build the graph once and process every course and edge.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time: O(V + E)
Space: O(V + E)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-2&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is probably the most interesting visualization of the three.&lt;/p&gt;
&lt;p&gt;Because there are several things changing together.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;graph
indegree
queue
head
completed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I only read&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;indegree[next]--;
if (indegree[next] === 0) queue.push(next);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I understand the syntax.&lt;/p&gt;
&lt;p&gt;But I may still ask:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why did this course become available now?&lt;/li&gt;
&lt;li&gt;Which prerequisite was removed?&lt;/li&gt;
&lt;li&gt;Why is this course still not in the queue?&lt;/li&gt;
&lt;li&gt;What does completed tell us?&lt;/li&gt;
&lt;li&gt;Where exactly does the cycle get stuck?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/704gnr0ul659berpdgng.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiY291cnNlLXNjaGVkdWxlIiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiY291cnNlLXNjaGVkdWxlIiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we view the runtime, we can watch the dependencies disappear.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0 → 1 → 2

indegree = [0, 1, 1]
queue = [0]

       ↓ finish 0

indegree = [0, 0, 1]
queue = [0, 1]

       ↓ finish 1

indegree = [0, 0, 0]
queue = [0, 1, 2]

       ↓ finish 2

completed = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code stops feeling like mysterious bookkeeping.&lt;/p&gt;
&lt;p&gt;We're really doing one simple thing:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Keep taking courses that are ready, and make their dependent courses closer to being ready.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If eventually every course becomes ready&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed === numCourses
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;there is no blocking cycle.&lt;/p&gt;
&lt;p&gt;If some courses never become ready&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;completed &amp;lt; numCourses
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;something is stuck in a cycle. 🎓😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-did-we-actually-learn-&quot;&gt;🧠 What Did We Actually Learn?&lt;/h3&gt;
&lt;p&gt;These three problems look very different.&lt;/p&gt;
&lt;p&gt;But each one teaches a useful way of thinking.&lt;/p&gt;
&lt;h4 id=&quot;number-of-islands&quot;&gt;Number of Islands&lt;/h4&gt;
&lt;p&gt;When you find one part of a connected group, explore the entire group before continuing.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;What else is connected to this?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;invert-binary-tree&quot;&gt;Invert Binary Tree&lt;/h4&gt;
&lt;p&gt;Let recursive calls solve the smaller subtrees first, then transform the current node using their results.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Can my children finish their work before I change this node?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;course-schedule&quot;&gt;Course Schedule&lt;/h4&gt;
&lt;p&gt;Process things that have no unresolved dependencies, then use them to unlock more work.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;What can I safely process right now?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementations are not very long.&lt;/p&gt;
&lt;p&gt;But each one introduces a different mental model.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;DFS on a grid
Recursive tree transformation
Topological sorting
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And once again, the syntax is not really the hardest part.&lt;/p&gt;
&lt;p&gt;The difficult part is keeping track of the changing state.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where are we?&lt;/li&gt;
&lt;li&gt;What changed?&lt;/li&gt;
&lt;li&gt;What is waiting?&lt;/li&gt;
&lt;li&gt;What has already been visited?&lt;/li&gt;
&lt;li&gt;Which recursive call are we inside?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes I can read every line and still lose the thread somewhere in the middle. 😿&lt;/p&gt;
&lt;p&gt;That's exactly when I want to &lt;strong&gt;view it&lt;/strong&gt;. 👀👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion&quot;&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we looked at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Number of Islands with recursive grid traversal&lt;/li&gt;
&lt;li&gt;Invert Binary Tree with recursion&lt;/li&gt;
&lt;li&gt;Course Schedule with topological sorting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more importantly, we followed what changed while each algorithm was running.&lt;/p&gt;
&lt;p&gt;For Number of Islands, we watched connected land disappear as it became visited.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Invert Binary Tree, we watched recursive calls go down and the tree change while they returned.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;go down
↓
come back
↓
swap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Course Schedule, we watched prerequisites disappear and new courses enter the queue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;indegree--
↓
0 prerequisites
↓
queue.push()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is exactly the kind of thing I built &lt;strong&gt;DSA View View&lt;/strong&gt; for.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app&quot;&gt;https://dsa-view-view.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can write or load a TypeScript implementation, run it with your own inputs, and move backward and forward through the runtime.&lt;/p&gt;
&lt;p&gt;If you are learning DSA too, try viewing one of these problems step by step.&lt;/p&gt;
&lt;p&gt;Especially when the implementation looks short, but your brain still says&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Wait... what just changed? 😿&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Seeing the runtime may make the idea much easier to follow.&lt;/p&gt;
&lt;p&gt;And if there is a DSA problem you want me to cover next, please let me know in the comments!&lt;/p&gt;
&lt;p&gt;I still have many algorithms to learn myself. 😸&lt;/p&gt;
&lt;p&gt;Let's train our DSA muscles together! 💪&lt;/p&gt;
&lt;p&gt;If you like DSA View View, please give it a star ⭐&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See you in the next article!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26218/learn-number-of-islands-invert-binary-tree-and-course-schedule-with-step-by-step</guid>
<pubDate>Fri, 04 Sep 2026 16:08:26 +0000</pubDate>
</item>
<item>
<title>🛡️ Rate Limiting in Web Applications</title>
<link>https://coderlegion.com/25794/rate-limiting-in-web-applications</link>
<description>&lt;p&gt;In today’s API-driven world, applications face thousands—or even millions—of requests every day. While high traffic is great, it can also create &lt;strong&gt;challenges&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🔒 Security risks (brute force, DDoS attacks)&lt;/li&gt;
&lt;li&gt;⚖️ Fair usage enforcement (avoid abuse of free tiers)&lt;/li&gt;
&lt;li&gt;🚀 Performance stability (prevent one user from hogging resources)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is where &lt;strong&gt;Rate Limiting&lt;/strong&gt; comes in.&lt;/p&gt;
&lt;p&gt;Rate limiting ensures &lt;strong&gt;a user, IP or client can only make a defined number of requests within a specific time window&lt;/strong&gt;. It’s a cornerstone of modern, scalable APIs.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-why-do-we-need-rate-limiting-&quot;&gt;🧩 Why Do We Need Rate Limiting?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;🔐 &lt;strong&gt;Security&lt;/strong&gt; → Protect against brute force login attempts, API scraping, and DDoS attacks.&lt;/li&gt;
&lt;li&gt;⚖️ &lt;strong&gt;Fair Usage&lt;/strong&gt; → Prevent abuse of APIs, especially for freemium services.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Performance&lt;/strong&gt; → Ensure backend resources are shared fairly among all users.&lt;/li&gt;
&lt;li&gt;💰 &lt;strong&gt;Cost Control&lt;/strong&gt; → Reduce infrastructure bills by blocking excessive or abusive requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-common-rate-limiting-algorithms&quot;&gt;⚙️ Common Rate Limiting Algorithms&lt;/h3&gt;
&lt;h4 id=&quot;1-token-bucket&quot;&gt;1️⃣ Token Bucket&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Requests are allowed if tokens are available.&lt;/li&gt;
&lt;li&gt;Tokens refill at a fixed rate.&lt;/li&gt;
&lt;li&gt;Commonly used (flexible + efficient).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🔧 Example: Allow 10 requests per second. If unused, tokens accumulate up to a max limit (burst handling).&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;2-leaky-bucket&quot;&gt;2️⃣ Leaky Bucket&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Works like water dripping from a bucket at a fixed rate.&lt;/li&gt;
&lt;li&gt;Bursts are smoothed out.&lt;/li&gt;
&lt;li&gt;Useful for evenly distributing traffic.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;3-fixed-window-counter&quot;&gt;3️⃣ Fixed Window Counter&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Count requests in a fixed time window (e.g., 100 requests per minute).&lt;/li&gt;
&lt;li&gt;❌ Edge case: allows bursts at window boundaries.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;4-sliding-window-log&quot;&gt;4️⃣ Sliding Window Log&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Keeps timestamps of requests.&lt;/li&gt;
&lt;li&gt;Precise but memory-heavy for large scale.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;5-sliding-window-counter&quot;&gt;5️⃣ Sliding Window Counter&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Hybrid approach: averages counts across windows.&lt;/li&gt;
&lt;li&gt;More accurate than fixed window, less heavy than logs.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-implementing-rate-limiting-in-applications&quot;&gt;🔧 Implementing Rate Limiting in Applications&lt;/h3&gt;
&lt;h4 id=&quot;example-in-go-gin-framework-&quot;&gt;Example in &lt;strong&gt;Go (Gin Framework)&lt;/strong&gt;&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;package main

import (
&quot;github.com/gin-gonic/gin&quot;
&quot;golang.org/x/time/rate&quot;
&quot;net/http&quot;
&quot;time&quot;
)

func main() {
r := gin.Default()

// Create a limiter: 5 requests/sec, burst up to 10
limiter := rate.NewLimiter(5, 10)

r.GET(&quot;/api&quot;, func(c *gin.Context) {
if !limiter.Allow() {
c.JSON(http.StatusTooManyRequests, gin.H{&quot;error&quot;: &quot;Too many requests&quot;})
return
}
c.JSON(http.StatusOK, gin.H{&quot;message&quot;: &quot;Request successful&quot;})
})

r.Run(&quot;:8080&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;✅ Each client is limited to &lt;strong&gt;5 requests per second&lt;/strong&gt; with a burst allowance of 10.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-rate-limiting-with-nginx&quot;&gt;🚦 Rate Limiting with NGINX&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;NGINX&lt;/strong&gt; can handle rate limiting &lt;strong&gt;at the web server or reverse proxy level&lt;/strong&gt;, making it a powerful tool to protect your backend before requests hit your app.&lt;/p&gt;
&lt;h4 id=&quot;-how-it-works&quot;&gt;⚙️ How It Works&lt;/h4&gt;
&lt;p&gt;NGINX uses two main directives:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;limit_req_zone&lt;/code&gt;&lt;/strong&gt; → Defines a shared memory zone to track requests (by IP or custom key).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;limit_req&lt;/code&gt;&lt;/strong&gt; → Applies the limit to endpoints.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-basic-example&quot;&gt;🔧 Basic Example&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;http {
  # 1. Define rate limit zone (1 request/sec, burst 5)
  limit_req_zone $binary_remote_addr zone=api_limit:10m rate=1r/s;

  server {
    location /api/ {
      # 2. Apply rate limit
      limit_req zone=api_limit burst=5 nodelay;

      proxy_pass http://backend_service;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-explanation&quot;&gt;🔍 Explanation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;$binary_remote_addr&lt;/code&gt; → Track by client IP.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rate:&lt;/strong&gt; &lt;code&gt;1r/s&lt;/code&gt; → Allows &lt;strong&gt;1 request per second per IP&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Burst:&lt;/strong&gt; &lt;code&gt;5&lt;/code&gt; → Short bursts up to 5 requests allowed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;nodelay:&lt;/strong&gt; Burst requests are processed immediately.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-advantages&quot;&gt;✅ Advantages&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Filters traffic &lt;strong&gt;before hitting your backend&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Can apply limits &lt;strong&gt;per endpoint&lt;/strong&gt; (e.g., &lt;code&gt;/login&lt;/code&gt; stricter than &lt;code&gt;/products&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Lightweight &amp;amp; fast.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-logs-amp-monitoring&quot;&gt;📊 Logs &amp;amp; Monitoring&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Exceeded requests → logged as &lt;code&gt;503&lt;/code&gt; (Service Unavailable).&lt;/li&gt;
&lt;li&gt;Useful for tracking abuse &amp;amp; fine-tuning limits.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-considerations&quot;&gt;⚠️ Considerations&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Avoid overly strict global limits (may block valid traffic).&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;$realip_remote_addr&lt;/code&gt; when behind a proxy/load balancer.&lt;/li&gt;
&lt;li&gt;Always test before production rollout.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-best-practices-for-rate-limiting&quot;&gt;🔒 Best Practices for Rate Limiting&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;🎯 Apply stricter limits on &lt;strong&gt;sensitive endpoints&lt;/strong&gt; (like &lt;code&gt;/login&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;🌍 Use &lt;strong&gt;different limits per client type&lt;/strong&gt; (mobile app vs. server-to-server).&lt;/li&gt;
&lt;li&gt;🧩 Combine &lt;strong&gt;server-level (NGINX/API Gateway)&lt;/strong&gt; + &lt;strong&gt;app-level&lt;/strong&gt; checks.&lt;/li&gt;
&lt;li&gt;📊 Monitor logs &amp;amp; dashboards for &lt;strong&gt;blocked traffic trends&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;🚀 Use &lt;strong&gt;distributed stores&lt;/strong&gt; (Redis) for shared limits in multi-instance apps.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-bonus-tips&quot;&gt;💡 Bonus Tips&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;429 Status Code:&lt;/strong&gt; Always return HTTP &lt;code&gt;429 Too Many Requests&lt;/code&gt; with a helpful message.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Retry-After Header:&lt;/strong&gt; Tell clients when to retry. Example:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;HTTP/1.1 429 Too Many Requests
Retry-After: 60
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Graceful Degradation:&lt;/strong&gt; Don’t just block—offer reduced functionality for non-critical requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-final-thoughts-1&quot;&gt;🎯 Final Thoughts&lt;/h3&gt;
&lt;p&gt;Rate limiting is &lt;strong&gt;not just a performance tool&lt;/strong&gt;—it’s a &lt;strong&gt;security guard, cost saver and reliability booster&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Whether you use &lt;strong&gt;algorithms in code&lt;/strong&gt;, &lt;strong&gt;NGINX&lt;/strong&gt; or &lt;strong&gt;API Gateways (Kong, Apigee, AWS API Gateway)&lt;/strong&gt;, implementing proper rate limiting ensures:&lt;/p&gt;
&lt;p&gt;✔️ Fair usage&lt;br&gt;
✔️ Secure endpoints&lt;br&gt;
✔️ Scalable systems&lt;/p&gt;
&lt;p&gt;🚀 &lt;strong&gt;The bottom line?&lt;/strong&gt; Smart rate limiting makes APIs faster, safer, and fairer—for everyone.&lt;/p&gt;
&lt;p&gt;💬 What approach do you use for rate limiting in your backend? Have you tried using Redis or NGINX rules? Let’s discuss in the comments!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25794/rate-limiting-in-web-applications</guid>
<pubDate>Sun, 30 Aug 2026 06:19:02 +0000</pubDate>
</item>
<item>
<title>Learn Valid Parentheses, Reverse Linked List, and Tree Max Depth with Step-by-Step Visualization in</title>
<link>https://coderlegion.com/25625/learn-valid-parentheses-reverse-linked-list-and-tree-depth-with-step-step-visualization</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer who struggles to make game sounds. 😿&lt;/p&gt;
&lt;p&gt;Have you used &lt;strong&gt;DSA View View&lt;/strong&gt; already? 👀👀&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&quot;&gt;https://dev.to/nyaomaru/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view--djo&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;DSA View View allows you to understand DSA by visualizing how your implementation actually runs.&lt;/p&gt;
&lt;p&gt;In the previous article, we looked at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two Sum&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Bubble Sort&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort-374o&quot;&gt;https://dev.to/nyaomaru/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort-374o&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This time, let's try three more classic problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Valid Parentheses&lt;/li&gt;
&lt;li&gt;Reverse Linked List&lt;/li&gt;
&lt;li&gt;Maximum Depth of Binary Tree&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These problems introduce three very different ideas:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Stack
Pointer manipulation
Recursion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And all three can become confusing when we only stare at the final code.&lt;/p&gt;
&lt;p&gt;So let's view what actually happens. 👀&lt;/p&gt;
&lt;p&gt;I'm still learning DSA too, so let's learn together! 😸&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/mgeyqexqh7vugtw8mia3.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-valid-parentheses&quot;&gt;🥞 Valid Parentheses&lt;/h3&gt;
&lt;p&gt;Let's start with &lt;strong&gt;Valid Parentheses&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have this string:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;()[]{}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every opening bracket has a matching closing bracket.&lt;/p&gt;
&lt;p&gt;So this is valid. ✅&lt;/p&gt;
&lt;p&gt;But this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;([)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;is not valid. ❌&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Because the brackets close in the wrong order.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
  [
)
  ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;[&lt;/code&gt; should be closed before the &lt;code&gt;(&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So how can we keep track of that order?&lt;/p&gt;
&lt;h4 id=&quot;use-a-stack&quot;&gt;Use a Stack&lt;/h4&gt;
&lt;p&gt;A &lt;strong&gt;stack&lt;/strong&gt; follows a simple rule:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The last thing we put in is the first thing we take out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is called &lt;strong&gt;LIFO&lt;/strong&gt; (Last In / First Out)&lt;/p&gt;
&lt;p&gt;Imagine stacking plates.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    🍽️  ← remove first
    🍽️
    🍽️
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last plate we put on top is the first one we can take.&lt;/p&gt;
&lt;p&gt;Parentheses work in the same way.&lt;/p&gt;
&lt;p&gt;If we see:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
[
{
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;then the brackets must close in reverse order&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;}
]
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So a stack is a very natural fit.&lt;/p&gt;
&lt;p&gt;Here is the implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function isValid(s: string): boolean {
  const stack: string[] = [];

  const pairs: Record&amp;lt;string, string&amp;gt; = {
    &quot;)&quot;: &quot;(&quot;,
    &quot;]&quot;: &quot;[&quot;,
    &quot;}&quot;: &quot;{&quot;,
  };

  for (const char of s) {
    if (char === &quot;(&quot; || char === &quot;[&quot; || char === &quot;{&quot;) {
      stack.push(char);
      continue;
    }

    if (stack.length === 0) return false;

    const target = stack.pop();
    if (target !== pairs[char]) {
      return false;
    }
  }

  return stack.length === 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part is what happens to &lt;code&gt;stack&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let's use&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;([])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We see&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's an opening bracket.&lt;/p&gt;
&lt;p&gt;Push it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = [&quot;(&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Push again.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = [&quot;(&quot;, &quot;[&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a closing bracket.&lt;/p&gt;
&lt;p&gt;What should it close?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And what is currently on top of the stack?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Perfect. ✅&lt;/p&gt;
&lt;p&gt;So we remove it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = [&quot;(&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It should close&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The top of the stack is also&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pop!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack = []
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We reached the end with an empty stack.&lt;/p&gt;
&lt;p&gt;Valid! 🎉&lt;/p&gt;
&lt;h4 id=&quot;what-about-an-invalid-example-&quot;&gt;What About an Invalid Example?&lt;/h4&gt;
&lt;p&gt;Consider&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;([)]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start the same way.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
↓
stack = [&quot;(&quot;]

[
↓
stack = [&quot;(&quot;, &quot;[&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we find&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;)&lt;/code&gt; needs&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the top of our stack is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They don't match.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;expected: (
actual:   [
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we immediately know the string is invalid.&lt;/p&gt;
&lt;h4 id=&quot;complexity-3&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;We walk through the string once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n)
Space: O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the worst case, the stack may contain all opening brackets.&lt;/p&gt;
&lt;h4 id=&quot;-let-s-view-view-it-3&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is where the stack becomes much easier to understand.&lt;/p&gt;
&lt;p&gt;When we only read.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;stack.push(char);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;stack.pop();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It can be easy to lose track of what is actually inside the stack.&lt;/p&gt;
&lt;p&gt;Especially with something like.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;({[]})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is on top right now?&lt;/p&gt;
&lt;p&gt;Which opening bracket are we trying to close?&lt;/p&gt;
&lt;p&gt;Instead of remembering everything in our head, we can follow the stack changing step by step.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/dcu4tkvrfug2d00z8e61.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidmFsaWQtcGFyZW50aGVzZXMiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidmFsaWQtcGFyZW50aGVzZXMiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Conceptually, we can see:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;(
↓
[(]

{
↓
[(, {]

[
↓
[(, {, []

]
↓
[(, {]

}
↓
[(]

)
↓
[]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's the whole idea.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Remember the opening brackets, and always match the most recent one first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Stack suddenly feels much less mysterious. 🥞😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-reverse-linked-list&quot;&gt;🔗 Reverse Linked List&lt;/h3&gt;
&lt;p&gt;Next, let's reverse a linked list.&lt;/p&gt;
&lt;p&gt;Suppose we have&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2 → 3 → 4 → 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5 → 4 → 3 → 2 → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first glance, this sounds simple.&lt;/p&gt;
&lt;p&gt;Just reverse it!&lt;/p&gt;
&lt;p&gt;But linked lists are a little different from arrays.&lt;/p&gt;
&lt;p&gt;With an array, the values live in positions like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0  1  2  3  4
↓  ↓  ↓  ↓  ↓
1  2  3  4  5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A linked list instead consists of nodes pointing to the next node.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2 → 3 → 4 → 5 → null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each arrow matters. To reverse the list, we need to reverse those arrows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 ← 2 ← 3 ← 4 ← 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this is where things can get confusing.&lt;/p&gt;
&lt;p&gt;Because if we change an arrow too early...&lt;/p&gt;
&lt;p&gt;we may lose the rest of the list. 😿&lt;/p&gt;
&lt;h4 id=&quot;three-important-variables&quot;&gt;Three Important Variables&lt;/h4&gt;
&lt;p&gt;A common iterative solution uses three variables&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev
current
next
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function reverseList(head: ListNode | null): ListNode | null {
  let prev: ListNode | null = null;
  let current = head;

  while (current !== null) {
    const next = current.next;

    current.next = prev;

    prev = current;
    current = next;
  }

  return prev;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's short.&lt;/p&gt;
&lt;p&gt;But there is a lot happening inside these few lines.&lt;/p&gt;
&lt;p&gt;Let's follow it carefully.&lt;/p&gt;
&lt;p&gt;We start with&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2 → 3 → null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev = null
current = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-1-save-the-next-node&quot;&gt;Step 1: Save the Next Node&lt;/h4&gt;
&lt;p&gt;First&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const next = current.next;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;next = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why do we need this?&lt;/p&gt;
&lt;p&gt;Because we're about to change&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we change that arrow without remembering &lt;code&gt;2&lt;/code&gt;, we lose access to the rest of the list.&lt;/p&gt;
&lt;p&gt;So first&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Save where we need to go next.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;step-2-reverse-the-arrow&quot;&gt;Step 2: Reverse the Arrow&lt;/h4&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;current.next = prev;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Originally&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But &lt;code&gt;prev&lt;/code&gt; is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first arrow has been reversed.&lt;/p&gt;
&lt;h4 id=&quot;step-3-move-prev&quot;&gt;Step 3: Move &lt;code&gt;prev&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Next&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;prev = current;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-4-move-current&quot;&gt;Step 4: Move &lt;code&gt;current&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Finally&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;current = next;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We saved &lt;code&gt;2&lt;/code&gt; earlier.&lt;/p&gt;
&lt;p&gt;So now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;current = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our state looks like this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null ← 1    2 → 3 → null
       ↑    ↑
      prev current
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we do exactly the same thing again.&lt;/p&gt;
&lt;p&gt;Save:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;next = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reverse&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2 → 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev = 2
current = 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null ← 1 ← 2    3 → null
           ↑    ↑
          prev  current
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One more time&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;next = null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reverse&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3 → 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev = 3
current = null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null ← 1 ← 2 ← 3
               ↑
              prev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The loop stops because&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;current === null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;prev&lt;/code&gt; is the new head.&lt;/p&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;return prev;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done! 🎉&lt;/p&gt;
&lt;h4 id=&quot;complexity-4&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;We visit each node once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n)
Space: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don't create another linked list.&lt;/p&gt;
&lt;p&gt;We only move a few pointers.&lt;/p&gt;
&lt;h4 id=&quot;-let-s-view-view-it-4&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is exactly the kind of code that I find difficult to understand by reading alone.&lt;/p&gt;
&lt;p&gt;These four lines:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const next = current.next;
current.next = prev;
prev = current;
current = next;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;look simple.&lt;/p&gt;
&lt;p&gt;But when I first see code like this, my brain starts asking.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Wait.

- Where is current now?
- Did we lose next?
- Which arrow changed?
- What exactly does prev point to?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;😿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/heoj8swgfccmhqe70pya.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHJlZS1kZXB0aCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoicmV2ZXJzZS1saXN0IiwibCI6InR5cGVzY3JpcHQiLCJtIjoidmVyaWZpY2F0aW9uIiwidiI6MX0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we visualize each step, we can actually follow the pointers moving.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev      current
 ↓           ↓
null         1 → 2 → 3

      ↓↓↓

null ← 1     2 → 3
       ↑     ↑
      prev current

      ↓↓↓

null ← 1 ← 2     3
           ↑     ↑
          prev current

      ↓↓↓

null ← 1 ← 2 ← 3
               ↑
              prev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The algorithm becomes much simpler when we stop thinking of it as four mysterious assignments.&lt;/p&gt;
&lt;p&gt;It's really just&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Save next
  ↓
Reverse arrow
  ↓
Move prev
  ↓
Move current
  ↓
Repeat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nice! 🔗😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-maximum-depth-of-binary-tree&quot;&gt;🌳 Maximum Depth of Binary Tree&lt;/h3&gt;
&lt;p&gt;Finally, let's look at a tree.&lt;/p&gt;
&lt;p&gt;Consider this binary tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;        3
       / \
      9   20
         /  \
        15   7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What is its maximum depth?&lt;/p&gt;
&lt;p&gt;The longest path from the root to a leaf contains three nodes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3
↓
20
↓
15
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the answer is.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;How can we calculate that?&lt;/p&gt;
&lt;h4 id=&quot;think-about-a-smaller-tree&quot;&gt;Think About a Smaller Tree&lt;/h4&gt;
&lt;p&gt;Suppose we are standing at one node.&lt;/p&gt;
&lt;p&gt;We don't really need to understand the entire tree at once.&lt;/p&gt;
&lt;p&gt;We only need to ask.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;How deep is the left subtree?

How deep is the right subtree?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then choose the larger one.&lt;/p&gt;
&lt;p&gt;And add &lt;code&gt;1&lt;/code&gt; for the current node.&lt;/p&gt;
&lt;p&gt;That's exactly what this implementation does.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function maxDepth(root: TreeNode | null): number {
  if (root === null) {
    return 0;
  }

  const leftDepth = maxDepth(root.left);
  const rightDepth = maxDepth(root.right);

  return Math.max(leftDepth, rightDepth) + 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important idea is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;Math.max(leftDepth, rightDepth) + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But recursion can feel strange.&lt;/p&gt;
&lt;p&gt;When we call&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;maxDepth(root.left);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where does the current function go?&lt;/p&gt;
&lt;p&gt;And how do all those calls eventually become one number?&lt;/p&gt;
&lt;p&gt;Let's follow a small example.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;    1
   / \
  2   3
 /
4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We start at&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But before &lt;code&gt;1&lt;/code&gt; can know its depth, it asks its left child&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;maxDepth(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node &lt;code&gt;2&lt;/code&gt; asks&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;maxDepth(4)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Node &lt;code&gt;4&lt;/code&gt; has no children.&lt;/p&gt;
&lt;p&gt;So both sides eventually reach&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;maxDepth(null);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;returns&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Therefore node &lt;code&gt;4&lt;/code&gt; can calculate&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;max(0, 0) + 1
= 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we return to node &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Its left side has depth&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Its right side is &lt;code&gt;null&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;max(1, 0) + 1
= 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now return to node &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Eventually its right subtree also returns&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So node &lt;code&gt;1&lt;/code&gt; gets&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;leftDepth = 2
rightDepth = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And calculates&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;max(2, 1) + 1
= 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Answer&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;🎉&lt;/p&gt;
&lt;h4 id=&quot;the-interesting-part-going-down-and-coming-back-up&quot;&gt;The Interesting Part: Going Down and Coming Back Up&lt;/h4&gt;
&lt;p&gt;This is what makes recursion interesting.&lt;/p&gt;
&lt;p&gt;First, the function calls go &lt;strong&gt;down&lt;/strong&gt; the tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
↓
2
↓
4
↓
null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the answers are built while returning &lt;strong&gt;back up&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;null → 0
4    → 1
2    → 2
1    → 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So recursion isn't only&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Keep calling the same function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are really two directions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Go down
  ↓
Reach the base case
  ↓
Return values back up
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The base case here is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (root === null) {
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without it, the recursion would have no place to stop.&lt;/p&gt;
&lt;h4 id=&quot;complexity-5&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;Every node is visited once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time: O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The recursive call stack depends on the height of the tree.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Space: O(h)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where &lt;code&gt;h&lt;/code&gt; is the height of the tree.&lt;/p&gt;
&lt;p&gt;For a balanced tree, that is roughly&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;O(log n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the worst case, if the tree looks like a linked list&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1
 \
  2
   \
    3
     \
      4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the depth can become&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-5&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;Recursion is probably my favorite example for visualization.&lt;/p&gt;
&lt;p&gt;Because the final implementation is tiny&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const leftDepth = maxDepth(root.left);
const rightDepth = maxDepth(root.right);

return Math.max(leftDepth, rightDepth) + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But a lot is hidden inside those function calls.&lt;/p&gt;
&lt;p&gt;When reading the code, it can feel like&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;maxDepth()
inside maxDepth()
inside maxDepth()
inside maxDepth()
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Where are we now? 😿&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/wldufg2fqqrb9w366k5y.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHJlZS1kZXB0aCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHJlZS1kZXB0aCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When we step through the execution, we can follow both parts&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Going down

1
↓
2
↓
4
↓
null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Coming back

null → 0
↓
4 → 1
↓
2 → 2
↓
1 → 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That makes the recursive idea much easier to see.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Ask the smaller subproblems for their answers, then use those answers to build the current answer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;🌳😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-did-we-actually-learn--1&quot;&gt;🧠 What Did We Actually Learn?&lt;/h3&gt;
&lt;p&gt;These three problems look completely different.&lt;/p&gt;
&lt;p&gt;But each one introduces a very useful way of thinking.&lt;/p&gt;
&lt;h4 id=&quot;valid-parentheses&quot;&gt;Valid Parentheses&lt;/h4&gt;
&lt;p&gt;Use a stack when the most recent item needs to be handled first.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;What was the last thing I opened?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;reverse-linked-list&quot;&gt;Reverse Linked List&lt;/h4&gt;
&lt;p&gt;When changing references, save what you still need before breaking the old connection.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Where do I need to go next before I change this pointer?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;maximum-depth-of-binary-tree&quot;&gt;Maximum Depth of Binary Tree&lt;/h4&gt;
&lt;p&gt;Break a problem into smaller versions of the same problem.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Can I get the answers from my children and build my answer from them?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is one reason I like learning these problems together.&lt;/p&gt;
&lt;p&gt;The implementations are not very large.&lt;/p&gt;
&lt;p&gt;But each one introduces a completely different mental model&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Stack
Pointer
Recursion
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And those mental models are much harder to learn than the syntax itself.&lt;/p&gt;
&lt;p&gt;Sometimes the code tells us &lt;strong&gt;what happens&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;But I also want to see &lt;strong&gt;how it happens&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I want to view it. 👀👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion-1&quot;&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we looked at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Valid Parentheses with a stack&lt;/li&gt;
&lt;li&gt;Reverse Linked List with pointer manipulation&lt;/li&gt;
&lt;li&gt;Maximum Depth of Binary Tree with recursion&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more importantly, we followed the state while each algorithm was running.&lt;/p&gt;
&lt;p&gt;We watched change the stack.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;stack.push() / stack.pop()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We watched move through a linked list.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;prev
current
next
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we watched recursive calls travel down a tree and return their answers back up.&lt;/p&gt;
&lt;p&gt;This is exactly the kind of thing I built &lt;strong&gt;DSA View View&lt;/strong&gt; for.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app&quot;&gt;https://dsa-view-view.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can write or load a TypeScript implementation, run it with your own inputs, and move backward and forward through the runtime.&lt;/p&gt;
&lt;p&gt;If you are learning DSA too, try viewing one of these problems step by step.&lt;/p&gt;
&lt;p&gt;Especially if a solution feels like&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I understand every line individually... but somehow I still don't understand the whole thing. 😿&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Seeing the runtime may connect those pieces together.&lt;/p&gt;
&lt;p&gt;And if there is a DSA problem you want me to cover next, please let me know in the comments!&lt;/p&gt;
&lt;p&gt;I still have many algorithms to learn myself. 😸&lt;/p&gt;
&lt;p&gt;Let's train our DSA muscles together! 💪&lt;/p&gt;
&lt;p&gt;If you like DSA View View, please give it a star ⭐&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See you in the next article!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25625/learn-valid-parentheses-reverse-linked-list-and-tree-depth-with-step-step-visualization</guid>
<pubDate>Fri, 28 Aug 2026 14:46:28 +0000</pubDate>
</item>
<item>
<title>Learning To Code Is A Memory Problem, Not A Video Problem</title>
<link>https://coderlegion.com/25571/learning-to-code-is-a-memory-problem-not-a-video-problem</link>
<description>&lt;p&gt;Most of us learned to code by watching somebody else do it, and then discovered that watching and doing are two different skills. The tools that moved the needle were never the ones that explained better, they were the ones that made me produce the answer from memory before showing it to me.&lt;/p&gt;
&lt;h3 id=&quot;the-tutorial-trap&quot;&gt;The Tutorial Trap&lt;/h3&gt;
&lt;p&gt;You follow a tutorial, everything works, and you feel like you understand it. A week later you open a blank file and cannot remember the shape of the thing you just built. That gap has a name in learning research: recognition is not recall. Reading code you have already seen feels fluent, and fluency is a terrible predictor of whether you can write it again.&lt;/p&gt;
&lt;p&gt;The uncomfortable version works better. Close the tab, write the thing from memory, get it wrong, then check. It feels worse and it is measurably more effective, which is exactly why almost nobody does it voluntarily.&lt;/p&gt;
&lt;h3 id=&quot;turn-the-docs-into-questions&quot;&gt;Turn The Docs Into Questions&lt;/h3&gt;
&lt;p&gt;This is where the current crop of AI study tools is genuinely useful, and it has nothing to do with them writing code for you. Feed a spec, an RFC, or a page of docs into a quiz generator and you get questions about the material back instead of a summary of it. You then have to answer them, which is the whole point.&lt;/p&gt;
&lt;p&gt;The same idea covers everything that is pure memorization with no logic to reason from: shell flags, SQL window function syntax, the git subcommands you touch twice a year, HTTP status codes past the obvious ones. A flashcard generator built from your own notes beats a public deck, because it uses your wording and it targets your gaps rather than somebody else's.&lt;/p&gt;
&lt;h3 id=&quot;solvers-that-show-their-work&quot;&gt;Solvers That Show Their Work&lt;/h3&gt;
&lt;p&gt;There is a real difference between a tool that returns an answer and a tool that returns the steps. For anything with math underneath it, and that now includes complexity analysis, query planning, and most of the graphics and ML work people are moving into, the steps are the entire lesson. The answer is worth nothing to you a week later.&lt;/p&gt;
&lt;p&gt;The practical test when picking one: can you cover the last line and predict it? If yes, the tool is teaching. If the output is a black box with a number at the bottom, you have outsourced the exact part you were trying to learn.&lt;/p&gt;
&lt;h3 id=&quot;where-summarizers-help-and-where-they-hurt&quot;&gt;Where Summarizers Help And Where They Hurt&lt;/h3&gt;
&lt;p&gt;Summarizers are triage. They are very good at telling you whether a forty page paper is worth your afternoon, and very bad at putting that paper in your head. Use one to decide what to read, never as a substitute for reading the thing you decided mattered.&lt;/p&gt;
&lt;h3 id=&quot;the-takeaway&quot;&gt;The Takeaway&lt;/h3&gt;
&lt;p&gt;Every one of these tools shares a failure mode. If it removes the effort, it removed the learning along with it. Pick the ones that hand the work back to you.&lt;/p&gt;
&lt;p&gt;I keep a running comparison of the current crop, quiz generators, flashcard makers, math solvers and the rest, in this guide to &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.aitools9.com/study-tools/&quot;&gt;AI study tools&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25571/learning-to-code-is-a-memory-problem-not-a-video-problem</guid>
<pubDate>Fri, 28 Aug 2026 06:01:50 +0000</pubDate>
</item>
<item>
<title>Quantum Mechanics For Developers: The Physics Under The Hardware You Ship</title>
<link>https://coderlegion.com/25471/quantum-mechanics-for-developers-the-physics-under-the-hardware-you-ship</link>
<description>&lt;p&gt;Most of us here write code that runs on transistors, and almost none of us ever had a reason to ask why a transistor works at all. This is a short tour of the physics that answers that question, written for developers rather than for physicists.&lt;/p&gt;
&lt;p&gt;Quantum mechanics has a reputation problem among people who write software. It arrives as cats in boxes and particles in two places at once, which makes it sound like philosophy rather than engineering. It is neither. It is the most experimentally verified theory in physics, its predictions have never once failed, and it is the reason semiconductors, lasers and MRI machines exist at all.&lt;/p&gt;
&lt;h3 id=&quot;the-principles-that-do-the-work&quot;&gt;The Principles That Do The Work&lt;/h3&gt;
&lt;p&gt;Five ideas carry almost all of the weight.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quantization.&lt;/strong&gt; Energy comes in discrete packets. An electron in an atom is restricted to specific energy levels, like a staircase with no ramp between the steps. Planck found this in 1900 while working out why hot objects glow the colors they do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Wave-particle duality.&lt;/strong&gt; Send electrons through a crystal and they produce an interference pattern like water waves through two openings. Detect those same electrons on a screen and each one arrives as a single point. Neither description is complete on its own.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Superposition.&lt;/strong&gt; A system can sit in a combination of states until a measurement forces one outcome. This is not a statement about our ignorance of some hidden value. No theory that assigns pre-existing definite values to every property can reproduce all the quantum predictions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Entanglement.&lt;/strong&gt; Two particles become correlated such that neither has a state you can describe independently, at any separation. Einstein called it spooky action at a distance and believed it showed the theory was incomplete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tunneling.&lt;/strong&gt; A particle crosses an energy barrier it classically cannot cross, because the wave function decays through the barrier rather than stopping at it, and still has a nonzero value on the far side.&lt;/p&gt;
&lt;h3 id=&quot;the-experiments-that-forced-it&quot;&gt;The Experiments That Forced It&lt;/h3&gt;
&lt;p&gt;The double-slit experiment is the center of all of it. Particles sent one at a time build an interference pattern, which is wave behavior. Add a detector that records which slit each particle went through and the pattern disappears. Feynman said this single experiment contains the only mystery in quantum mechanics, because everything else is a variation of the same puzzle.&lt;/p&gt;
&lt;p&gt;The photoelectric effect settled the question of whether light is quantized. Shine light on a metal and electrons are ejected, but only above a threshold frequency, no matter how intense the light is. Einstein explained it in 1905 with photons carrying energy proportional to frequency, and it earned him the Nobel Prize in 1921.&lt;/p&gt;
&lt;p&gt;Stern-Gerlach in 1922 sent silver atoms through a non-uniform magnetic field and got two distinct beams instead of a continuous spread, which is the first direct evidence of quantum spin.&lt;/p&gt;
&lt;p&gt;Bell test experiments, from Alain Aspect in 1982 through the loophole-free versions in 2015, ruled out local hidden variable explanations for entanglement. That is about as close as physics gets to closing an argument.&lt;/p&gt;
&lt;h3 id=&quot;where-it-shows-up-in-your-stack&quot;&gt;Where It Shows Up In Your Stack&lt;/h3&gt;
&lt;p&gt;Semiconductors are quantum band structure applied at industrial scale, which means every transistor you have ever run code on is a quantum device. Flash memory writes and erases by tunneling electrons through an oxide layer. Lasers depend on quantized energy levels and stimulated emission. Scanning tunneling microscopes image individual atoms using tunneling current as the signal.&lt;/p&gt;
&lt;p&gt;And quantum computing treats superposition and entanglement as a computational resource rather than a curiosity, which is why the field cares so much about coherence times. If you want the computing angle specifically, the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.learnhowtoscience.com/quantum-computing-explained/&quot;&gt;quantum computing guide&lt;/a&gt; covers qubits, gates and error correction without the marketing.&lt;/p&gt;
&lt;h3 id=&quot;the-takeaway-1&quot;&gt;The Takeaway&lt;/h3&gt;
&lt;p&gt;You do not need the mathematics to get value out of the intuition. Knowing that energy is quantized, that measurement is not passive, and that tunneling is a real transport mechanism changes how you read hardware documentation and how much sense device physics makes. The full walkthrough of the principles, the experiments and the applications is in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.learnhowtoscience.com/quantum-mechanics-explained/&quot;&gt;quantum mechanics guide&lt;/a&gt;, and it is written for people coming in without a physics background.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25471/quantum-mechanics-for-developers-the-physics-under-the-hardware-you-ship</guid>
<pubDate>Wed, 26 Aug 2026 22:45:18 +0000</pubDate>
</item>
<item>
<title>C++ unique_ptr: reset() vs release() vs std::move</title>
<link>https://coderlegion.com/25146/c-uniqueptr-reset-vs-release-vs-std-move</link>
<description>&lt;p&gt;When you first learn &lt;code&gt;std::unique_ptr&lt;/code&gt;, three things confuse almost everyone: &lt;code&gt;release()&lt;/code&gt;, &lt;code&gt;reset()&lt;/code&gt;, and &lt;code&gt;std::move&lt;/code&gt;. They all look like they &quot;give something up&quot;, but they do very different things. This post explains what each one really does, with short examples.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-the-key-to-understanding-release-&quot;&gt;1. The key to understanding &lt;code&gt;release()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;release()&lt;/code&gt; means: &quot;I don't want to manage this object anymore.&quot;&lt;/p&gt;
&lt;p&gt;It does &lt;strong&gt;not&lt;/strong&gt; delete the object. It just gives up ownership and hands the raw pointer back to you. After the call, the smart pointer is empty (&lt;code&gt;get()&lt;/code&gt; returns &lt;code&gt;nullptr&lt;/code&gt;), and &lt;strong&gt;you&lt;/strong&gt; are now responsible for deleting the object.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;&lt;br&gt;
(None)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Return value&lt;/strong&gt;&lt;br&gt;
The raw pointer to the managed object, or &lt;code&gt;nullptr&lt;/code&gt; if there was no managed object.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;memory&amp;gt;
#include &amp;lt;iostream&amp;gt;

int main(){
std::unique_ptr&amp;lt;int&amp;gt; ptr = std::make_unique&amp;lt;int&amp;gt;(100);
int* raw = ptr.release(); // ptr gives up ownership, ptr is empty now
if(!ptr)
std::cout&amp;lt;&amp;lt; *raw &amp;lt;&amp;lt; std::endl;
delete raw; // we must delete raw manually
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Warning: &lt;code&gt;release()&lt;/code&gt; is rarely what you want:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If you forget to capture the returned raw pointer, the object leaks immediately.&lt;/li&gt;
&lt;li&gt;After taking the raw pointer, you must remember to &lt;code&gt;delete&lt;/code&gt; it yourself — you are back to manual raw-pointer management.&lt;/li&gt;
&lt;li&gt;The one legitimate use: handing the object to a legacy C API that requires a raw pointer and promises to take ownership.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-the-right-way-to-use-reset-&quot;&gt;2. The right way to use &lt;code&gt;reset()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;reset()&lt;/code&gt; replaces the managed object.&lt;/p&gt;
&lt;p&gt;Called with no argument, &lt;code&gt;ptr.reset()&lt;/code&gt; deletes the object it owns and becomes empty. If you pass in a new pointer, the old object is destroyed first, and the smart pointer then takes ownership of the new one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ptr&lt;/code&gt; - (optional) pointer to a new object to manage&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;memory&amp;gt;
#include &amp;lt;iostream&amp;gt;

int main(){
std::unique_ptr&amp;lt;int&amp;gt; ptr = std::make_unique&amp;lt;int&amp;gt;(100);

ptr.reset();  // delete the original object, ptr is empty.

ptr.reset(new int(10)); // delete the old object, manage the new pointer.
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-comparing-release-and-reset-&quot;&gt;3. Comparing &lt;code&gt;release()&lt;/code&gt; and &lt;code&gt;reset()&lt;/code&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;release()&lt;/code&gt;: does not delete the old object — it hands the raw pointer back to you&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reset()&lt;/code&gt;: deletes the old object (if any), then manages the new pointer you pass in&lt;/li&gt;
&lt;li&gt;Both make the smart pointer lose control of the old resource; the difference is who deletes the object and when&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;release() &lt;/th&gt;&lt;th&gt;reset()&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Old object destroyed? &lt;/td&gt;&lt;td&gt;No — you must delete it yourself &lt;/td&gt;&lt;td&gt;Yes, immediately&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Smart pointer after call &lt;/td&gt;&lt;td&gt;empty &lt;/td&gt;&lt;td&gt;manages the new pointer (or nothing)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Returns &lt;/td&gt;&lt;td&gt;the raw pointer &lt;/td&gt;&lt;td&gt;nothing&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-how-std-move-fits-in&quot;&gt;4. How &lt;code&gt;std::move&lt;/code&gt; fits in&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;std::move&lt;/code&gt; does not move anything by itself. It only marks an object as &quot;moved-from&quot; — a permission slip saying &quot;you may take this object's resources instead of copying them&quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Parameters&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ob&lt;/code&gt; - the object to be moved&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;class MyClass{
std::string data;
public:
MyClass(std::string&amp;amp;&amp;amp; str) : data(std::move(str)) {}
};
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;std::vector&amp;lt;std::string&amp;gt; v;
std::string temp = &quot;example&quot;;
v.push_back(std::move(temp));
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;lt;memory&amp;gt;
#include &amp;lt;iostream&amp;gt;

int main(){
std::unique_ptr&amp;lt;int&amp;gt; ptr1(new int(10));
std::unique_ptr&amp;lt;int&amp;gt; ptr2 = std::move(ptr1);
if(!ptr1)
std::cout&amp;lt;&amp;lt; &quot;ptr1 is empty&quot; &amp;lt;&amp;lt; std::endl;
if(ptr2)
std::cout&amp;lt;&amp;lt; &quot;ptr2 owns the resource and value is: &quot;&amp;lt;&amp;lt;*ptr2 &amp;lt;&amp;lt; std::endl;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Why is &lt;code&gt;std::move&lt;/code&gt; so common with smart pointers?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;std::unique_ptr&lt;/code&gt; has exclusive ownership: it cannot be copied, because two owners would delete the same object twice. So the only way to transfer ownership between &lt;code&gt;unique_ptr&lt;/code&gt;s is &lt;code&gt;std::move&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;references&quot;&gt;References&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.cppreference.com/w/cpp/memory/unique_ptr/release&quot;&gt;std::unique_ptr::release — cppreference.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.cppreference.com/w/cpp/memory/unique_ptr/reset&quot;&gt;std::unique_ptr::reset — cppreference.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://en.cppreference.com/w/cpp/utility/move&quot;&gt;std::move — cppreference.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25146/c-uniqueptr-reset-vs-release-vs-std-move</guid>
<pubDate>Sat, 22 Aug 2026 15:48:55 +0000</pubDate>
</item>
<item>
<title>AI makes learning faster. But is faster always better ?</title>
<link>https://coderlegion.com/25014/ai-makes-learning-faster-but-is-faster-always-better</link>
<description>&lt;p&gt;AI is an incredible learning tool. I said it in my &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/josaphatstar/im-a-masters-student-in-ai-big-data-and-ai-just-gave-me-my-freedom-back-j9d&quot;&gt;previous article&lt;/a&gt;, and I still believe it.&lt;/p&gt;
&lt;p&gt;But after spending months learning new web frameworks and technologies with AI as my main companion, I noticed something uncomfortable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I was moving fast. But was I really understanding?&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-old-way-was-painful-and-powerful&quot;&gt;The old way was painful and powerful&lt;/h3&gt;
&lt;p&gt;Before AI, learning a new technology meant suffering.&lt;/p&gt;
&lt;p&gt;You'd hit a bug. You'd search for hours. Read Stack Overflow threads, YouTube videos. Try things that didn't work. Get frustrated. Sleep on it. Come back the next day and finally &lt;em&gt;click&lt;/em&gt;. It works.&lt;/p&gt;
&lt;p&gt;That process was slow. Sometimes humiliating. Often exhausting.&lt;/p&gt;
&lt;p&gt;But here's what I didn't realize at the time: &lt;strong&gt;that struggle was doing something to my brain.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Every hour spent searching, failing, and retrying was building a deep, almost physical understanding of the concept. When I finally found the solution, I &lt;em&gt;owned&lt;/em&gt; it. I knew not just the answer, but every wrong path around it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;now-i-just-ask-and-get-an-answer-instantly-&quot;&gt;Now I just ask. And get an answer. Instantly.&lt;/h3&gt;
&lt;p&gt;With AI, that whole painful loop collapses into a single message.&lt;/p&gt;
&lt;p&gt;Bug? Ask AI. Confusing concept? Ask AI. Don't know the right syntax? Ask AI.&lt;/p&gt;
&lt;p&gt;The answer comes in seconds. Clean. Well-explained. Usually correct.&lt;/p&gt;
&lt;p&gt;And I move on.&lt;/p&gt;
&lt;p&gt;The problem is: &lt;strong&gt;moving on is not the same as understanding.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-i-actually-noticed&quot;&gt;What I actually noticed&lt;/h3&gt;
&lt;p&gt;Learning Vue, Nuxt, and other web technologies with AI, I realized something: &lt;strong&gt;I needed to build far more projects than before to reach the same depth of understanding.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Without AI, one difficult project could teach me something deeply because I had wrestled with every problem alone.&lt;/p&gt;
&lt;p&gt;With AI, I could finish that same project twice as fast, but the understanding was shallower. I had to build three or four more projects to reach the same level of genuine comprehension.&lt;/p&gt;
&lt;p&gt;The friction wasn't just wasted time. &lt;strong&gt;The friction was the learning.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;this-doesn-t-mean-ai-is-bad-for-learning&quot;&gt;This doesn't mean AI is bad for learning&lt;/h3&gt;
&lt;p&gt;I'm not saying we should go back to suffering alone for hours.&lt;/p&gt;
&lt;p&gt;What I'm saying is: &lt;strong&gt;we need to be intentional.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A few things that help me:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Try first, ask second.&lt;/strong&gt; Before asking AI, I give myself at least 15-20 minutes to struggle alone. The struggle primes my brain to actually absorb the answer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ask AI to explain, not just fix.&lt;/strong&gt; Instead of &quot;fix this bug&quot;, I ask &quot;why is this happening and what does it teach me about how this framework works?&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build more, copy less.&lt;/strong&gt; More small projects from scratch. Less copy-pasting AI-generated code without understanding it.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-bottom-line&quot;&gt;The bottom line&lt;/h3&gt;
&lt;p&gt;AI accelerates learning. But depth still comes from doing really doing and from the discomfort of not knowing yet.&lt;/p&gt;
&lt;p&gt;The goal isn't to avoid AI. It's to use it without losing the struggle that makes knowledge stick.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Have you felt this too? Are you learning faster but sometimes feeling like things don't fully sink in? Let's talk in the comments.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Follow my journey on &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.facebook.com/profile.php?id=61585926839187&quot;&gt;Facebook&lt;/a&gt; I share thoughts on tech, learning, and building in the open.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25014/ai-makes-learning-faster-but-is-faster-always-better</guid>
<pubDate>Thu, 20 Aug 2026 20:19:05 +0000</pubDate>
</item>
<item>
<title>Is Learning DSA Boring? Let's Use DSA View View 👀👀 (Two Sum, Binary Search, and Bubble Sort)</title>
<link>https://coderlegion.com/24928/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer who dislikes crowded places, so I'm planning to take a quiet vacation in September. 🏝️&lt;/p&gt;
&lt;p&gt;Have you used &lt;strong&gt;DSA View View&lt;/strong&gt; already? 👀👀&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/22758/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view&quot;&gt;https://coderlegion.com/22758/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;DSA View View allows you to understand DSA by visualizing how your implementation actually runs.&lt;/p&gt;
&lt;p&gt;But just introducing the tool is not enough.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Can it actually help us understand DSA?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this article, we'll walk through three classic problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two Sum&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Bubble Sort&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We'll first understand the algorithm, and then see what is actually happening with &lt;strong&gt;DSA View View&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/7olyo9iwv18govsyty9p.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;Let's learn together! 😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-two-sum&quot;&gt;🗺️ Two Sum&lt;/h3&gt;
&lt;p&gt;Let's start with a very famous problem.&lt;/p&gt;
&lt;p&gt;Given an array of numbers and a target value, find the indices of two numbers whose sum equals the target.&lt;/p&gt;
&lt;p&gt;For example,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums = [2, 7, 11, 15];
target = 9;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The answer is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;[0, 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2 + 7 = 9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple!&lt;/p&gt;
&lt;p&gt;So, how should we find them? 🤔&lt;/p&gt;
&lt;h4 id=&quot;brute-force&quot;&gt;Brute Force&lt;/h4&gt;
&lt;p&gt;The easiest approach is probably checking every possible pair.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function twoSum(nums: number[], target: number): number[] {
  for (let i = 0; i &amp;lt; nums.length; i++) {
    for (let j = i + 1; j &amp;lt; nums.length; j++) {
      if (nums[i] + nums[j] === target) {
        return [i, j];
      }
    }
  }

  return [];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works.&lt;/p&gt;
&lt;p&gt;But if the array becomes large, we may need to compare a lot of pairs, right?&lt;/p&gt;
&lt;p&gt;The time complexity is &lt;em&gt;O(n²)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Can we avoid checking the same values again and again?&lt;/p&gt;
&lt;p&gt;Yes.&lt;/p&gt;
&lt;p&gt;Let's use a &lt;code&gt;Map&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function twoSum(nums: number[], target: number): number[] {
  const seen = new Map&amp;lt;number, number&amp;gt;();

  for (let i = 0; i &amp;lt; nums.length; i++) {
    const current = nums[i];
    const need = target - current;

    if (seen.has(need)) {
      return [seen.get(need)!, i];
    }

    seen.set(current, i);
  }

  return [];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part is this 👇&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const need = target - current;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead of asking&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Which two numbers should I combine?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;we ask&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;What number do I need to complete the target?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let's follow the example.&lt;/p&gt;
&lt;p&gt;At first&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;current = 2
target = 9

need = 9 - 2
     = 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Have we already seen &lt;code&gt;7&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;p&gt;So we remember &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;seen = {
  2 → 0
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;current = 7
target = 9

need = 9 - 7
     = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Have we already seen &lt;code&gt;2&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Yes! 👀👀&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;seen = {
  2 → 0
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;return [0, 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done!&lt;/p&gt;
&lt;p&gt;Because we only need to walk through the array once, the time complexity becomes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n)
Space: O(n)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-let-s-view-view-it-6&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;The implementation is quite small.&lt;/p&gt;
&lt;p&gt;But when I was first learning this pattern, this part still felt a little magical.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;if (seen.has(need))
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Where did &lt;code&gt;need&lt;/code&gt; come from?&lt;/li&gt;
&lt;li&gt;What is inside &lt;code&gt;seen&lt;/code&gt; at this moment?&lt;/li&gt;
&lt;li&gt;Why does checking the previous values solve the problem?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is exactly where visualization helps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/vlxo25nh00u0exqukl5p.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHdvLXN1bSIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoidHdvLXN1bSIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;With DSA View View, we can move through the runtime one step at a time and inspect how the values change.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;2
↓
Need 7
↓
Remember 2
↓
7
↓
Need 2
↓
Found 2!
🎉
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the &lt;code&gt;Map&lt;/code&gt; is not just some mysterious trick.&lt;/p&gt;
&lt;p&gt;We can actually follow the idea.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Remember what we have already seen, and check whether the value we need is there.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Nice! 😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-binary-search&quot;&gt;🔍 Binary Search&lt;/h3&gt;
&lt;p&gt;Next is &lt;strong&gt;Binary Search&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Suppose we have this sorted array&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 3, 5, 7, 9, 11, 13]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we want to find&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, we could start from &lt;code&gt;1&lt;/code&gt; and check every number.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1 → 3 → 5 → 7 → 9 → 11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That works.&lt;/p&gt;
&lt;p&gt;But Binary Search does something smarter.&lt;/p&gt;
&lt;p&gt;Instead of checking from the beginning, it checks the &lt;strong&gt;middle&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 3, 5, 7, 9, 11, 13]
          ↑
         mid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our middle value is &lt;code&gt;7&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We are looking for &lt;code&gt;11&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;11 &amp;gt; 7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the array is sorted, we already know something very useful.&lt;/p&gt;
&lt;p&gt;Everything on the left side of &lt;code&gt;7&lt;/code&gt; is also smaller than &lt;code&gt;11&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, we don't need that half anymore. 👋&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 3, 5, 7, 9, 11, 13]
             └───────┘
               search
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we check the middle of the remaining range.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[9, 11, 13]
     ↑
    mid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;11 === 11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Found it! 🎉&lt;/p&gt;
&lt;p&gt;Here is the implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function binarySearch(nums: number[], target: number): number {
  let left = 0;
  let right = nums.length - 1;

  while (left &amp;lt;= right) {
    const mid = Math.floor((left + right) / 2);

    if (nums[mid] === target) {
      return mid;
    }

    if (nums[mid] &amp;lt; target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return -1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are three important variables.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left
right
mid
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They represent the current search range.&lt;/p&gt;
&lt;p&gt;For our example, they start like this&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;left = 0
right = 6
mid = 3

[1, 3, 5, 7, 9, 11, 13]
 ↑        ↑          ↑
left     mid       right
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums[mid] &amp;lt; target;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we move &lt;code&gt;left&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;left = mid + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 3, 5, 7, 9, 11, 13]
             ↑   ↑   ↑
           left mid right
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;code&gt;11&lt;/code&gt; is found.&lt;/p&gt;
&lt;h4 id=&quot;why-is-binary-search-fast-&quot;&gt;Why Is Binary Search Fast?&lt;/h4&gt;
&lt;p&gt;This is the interesting part.&lt;/p&gt;
&lt;p&gt;Each step removes about &lt;strong&gt;half of the remaining candidates&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If there are 1,000 values, we don't necessarily need 1,000 checks.&lt;/p&gt;
&lt;p&gt;It becomes roughly&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1000
↓
500
↓
250
↓
125
↓
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's why Binary Search has&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(log n)
Space: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But there is one very important condition.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The data must be sorted.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Without sorted data, we cannot safely throw away half of the search range.&lt;/p&gt;
&lt;h4 id=&quot;-let-s-view-view-it-7&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;Binary Search is one of the algorithms that made me want a visualization tool in the first place.&lt;/p&gt;
&lt;p&gt;The code itself is short&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;left = mid + 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;right = mid - 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Easy.&lt;/p&gt;
&lt;p&gt;But when learning it, I sometimes found myself thinking:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Wait... which part are we searching now? 😿&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ib05yz7hx7l1ho2pmbgw.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiYmluYXJ5LXNlYXJjaCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiYmluYXJ5LXNlYXJjaCIsImwiOiJ0eXBlc2NyaXB0IiwibSI6InZlcmlmaWNhdGlvbiIsInYiOjF9&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;When we visualize &lt;code&gt;left&lt;/code&gt;, &lt;code&gt;mid&lt;/code&gt;, and &lt;code&gt;right&lt;/code&gt;, the idea becomes much easier to follow.&lt;/p&gt;
&lt;p&gt;We are not randomly changing three numbers.&lt;/p&gt;
&lt;p&gt;We are continuously shrinking the search area.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;███████████████

       ↓

        ███████

       ↓

          ███

       ↓

           █
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's Binary Search!&lt;/p&gt;
&lt;p&gt;Cut the unnecessary half.&lt;/p&gt;
&lt;p&gt;Then cut it again. And again. And again.&lt;/p&gt;
&lt;p&gt;Until we find the answer. ✂️😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;bubble-sort&quot;&gt;Bubble Sort&lt;/h3&gt;
&lt;p&gt;Finally, let's sort something!&lt;/p&gt;
&lt;p&gt;Consider this array.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;[5, 1, 4, 2, 8];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We want.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;[1, 2, 4, 5, 8];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Bubble Sort repeatedly compares &lt;strong&gt;two neighboring values&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If they are in the wrong order, it swaps them.&lt;/p&gt;
&lt;p&gt;Let's look at the beginning.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[5, 1, 4, 2, 8]
 ↑  ↑
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compare&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5 &amp;gt; 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So swap them.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 5, 4, 2, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 5, 4, 2, 8]
    ↑  ↑
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;5 &amp;gt; 4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swap!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 4, 5, 2, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And continue.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 4, 5, 2, 8]
       ↑  ↑

5 &amp;gt; 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Swap!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 4, 2, 5, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eventually, larger values move toward the end of the array.&lt;/p&gt;
&lt;p&gt;They kind of...&lt;/p&gt;
&lt;p&gt;bubble up. &lt;/p&gt;
&lt;p&gt;That's why it is called &lt;strong&gt;Bubble Sort&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here is a simple implementation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function bubbleSort(nums: number[]): number[] {
  for (let i = 0; i &amp;lt; nums.length - 1; i++) {
    for (let j = 0; j &amp;lt; nums.length - i - 1; j++) {
      if (nums[j] &amp;gt; nums[j + 1]) {
        [nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
      }
    }
  }

  return nums;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We repeatedly compare&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums[j];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums[j + 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and swap them when necessary.&lt;/p&gt;
&lt;p&gt;After one full pass, the largest remaining value reaches its correct position near the end.&lt;/p&gt;
&lt;p&gt;So on the next pass, we don't need to check that position again.&lt;/p&gt;
&lt;p&gt;That's why the inner loop contains.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;nums.length - i - 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;complexity-6&quot;&gt;Complexity&lt;/h4&gt;
&lt;p&gt;Bubble Sort isn't very fast for large arrays.&lt;/p&gt;
&lt;p&gt;Its time complexity is&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Time:  O(n²)
Space: O(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So I probably won't suddenly replace production sorting with Bubble Sort tomorrow. 😸&lt;/p&gt;
&lt;p&gt;But as a learning example, I really like it.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;Because you can &lt;strong&gt;see the algorithm working&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;-let-s-view-view-it-8&quot;&gt;👀 Let's View View It&lt;/h4&gt;
&lt;p&gt;This is probably the most visually satisfying one of the three.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/7yw14rpesxucg4wdb0m7.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiYnViYmxlLXNvcnQiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&quot;&gt;https://dsa-view-view.vercel.app/#s=j.eyJlIjoiYnViYmxlLXNvcnQiLCJsIjoidHlwZXNjcmlwdCIsIm0iOiJ2ZXJpZmljYXRpb24iLCJ2IjoxfQ&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;Instead of only reading&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;we can follow the values moving through the array.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[5, 1, 4, 2, 8]

 ↓ swap

[1, 5, 4, 2, 8]

    ↓ swap

[1, 4, 5, 2, 8]

       ↓ swap

[1, 4, 2, 5, 8]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then another pass begins.&lt;/p&gt;
&lt;p&gt;The code contains nested loops, indexes, comparisons, and swaps.&lt;/p&gt;
&lt;p&gt;But visually, the basic rule is extremely simple:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Compare neighbors. If the left one is bigger, swap them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Repeat. Repeat. Repeat.&lt;/p&gt;
&lt;p&gt;Sorted! 🎉&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-did-we-actually-learn--2&quot;&gt;🧠 What Did We Actually Learn?&lt;/h3&gt;
&lt;p&gt;These three problems look quite different.&lt;/p&gt;
&lt;p&gt;But each one introduces a useful way of thinking.&lt;/p&gt;
&lt;h4 id=&quot;two-sum&quot;&gt;Two Sum&lt;/h4&gt;
&lt;p&gt;Remember information from previous steps.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Have I already seen what I need?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;binary-search&quot;&gt;Binary Search&lt;/h4&gt;
&lt;p&gt;Use what we already know to remove impossible candidates.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Can I safely discard half of the search space?
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;bubble-sort-1&quot;&gt;Bubble Sort&lt;/h4&gt;
&lt;p&gt;Break a larger problem into many small comparisons.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Are these two values in the correct order?
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is one of the things I find interesting about learning DSA.&lt;/p&gt;
&lt;p&gt;At first, the implementation can look like a collection of indexes, loops, conditions, and mysterious variables.&lt;/p&gt;
&lt;p&gt;But behind the code, there is usually a much simpler idea.&lt;/p&gt;
&lt;p&gt;And sometimes I don't fully understand that idea just by staring at the code.&lt;/p&gt;
&lt;p&gt;I want to &lt;strong&gt;view it&lt;/strong&gt;. 👀👀&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion-2&quot;&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we looked at three classic algorithms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Two Sum with a &lt;code&gt;Map&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Bubble Sort&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And more importantly, we looked at &lt;strong&gt;how the data changes while they run&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I think this is where visualization can be especially useful.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reading the final implementation tells us &lt;strong&gt;what the code is&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Stepping through it helps us understand &lt;strong&gt;why it works&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's exactly why I built DSA View View.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dsa-view-view.vercel.app&quot;&gt;https://dsa-view-view.vercel.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can write or load a TypeScript implementation, run it with your own inputs, and move backward and forward through the runtime.&lt;/p&gt;
&lt;p&gt;If you are learning DSA too, try taking a problem you already solved and viewing it step by step.&lt;/p&gt;
&lt;p&gt;You may notice something you didn't notice when only reading the code. 👀&lt;/p&gt;
&lt;p&gt;And if there is a DSA problem you want me to cover next, please let me know in the comments!&lt;/p&gt;
&lt;p&gt;I still have many algorithms to learn myself. 😸&lt;/p&gt;
&lt;p&gt;Let's train our DSA muscles together! 💪&lt;/p&gt;
&lt;p&gt;If you like DSA View View, please give it a star ⭐&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And my DSA View View has launched at TinyLaunch! 🚀 Please take a loot 👇&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.tinylaunch.com/launch/17804-dsa-view-view&quot;&gt;https://www.tinylaunch.com/launch/17804-dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See you in the next article!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24928/is-learning-dsa-boring-lets-use-dsa-view-view-two-sum-binary-search-and-bubble-sort</guid>
<pubDate>Wed, 19 Aug 2026 15:14:55 +0000</pubDate>
</item>
<item>
<title>Premature Optimization of Your Brain: Why Your Learning Roadmap Is Bad Architecture</title>
<link>https://coderlegion.com/24326/premature-optimization-of-your-brain-why-your-learning-roadmap-is-bad-architecture</link>
<description>&lt;p&gt;Picture the classic beginner. Fifty tabs open. One says &quot;Python Basics.&quot; Another says &quot;How to Set Up a Kubernetes Cluster on a Raspberry Pi With No SIM Card,&quot; which, sure, bud, that's definitely the blocker between you and your first job.&lt;/p&gt;
&lt;p&gt;This is the guy architecting a system for 100 million users before he's shipped a single line of working code. Before he has one client. Before he has anything except vibes and a folder called &quot;learning-roadmap-final-v3.&quot; He thinks preparation is the path. It isn't. It's just a really elaborate way of not starting. Every senior dev alive will tell you premature optimization is the root of all evil, nod sagely over their coffee, and then go home and do the exact same thing to their own brain — pre-allocating memory for variables that are never, ever going to be declared. Your skull is not a Kubernetes cluster. You don't need seventeen microservices of knowledge before you've written a monolith that actually runs.&lt;/p&gt;
&lt;p&gt;And the panic isn't coming from nowhere. Some YouTuber told you that if you don't know Docker, Next.js, and whatever's trending this week, you're basically unemployable, doomed, going to die alone eating instant noodles over a laptop that still has training wheels on. So instead of building the boring contact form you actually need — the one that would take you an afternoon — you go memorize a whole stack you have nowhere to put yet. I've watched a guy spend three weeks on Kubernetes YAML for a side project that gets, on a good day, forty visitors. Forty. It's still running on &lt;code&gt;python manage.py runserver&lt;/code&gt; energy underneath, he just wrapped it in enough infrastructure to survive a Black Friday it will never see. That's cargo cult programming, textbook definition. You're out there building an airplane out of straw and praying it takes off, because somewhere a guy in a suit told you the real planes have straw too. Learning tech &quot;just in case&quot; isn't ambition, it's anxiety wearing a hoodie, and the resource it burns fastest isn't time. It's focus — the one thing you actually needed for that contact form. Your learning stack currently looks like a government transit project — way over-budget and still doesn't work. What you needed was a bicycle. You built a jet you don't know how to fly and can't afford fuel for.&lt;/p&gt;
&lt;p&gt;So let's fix the actual root cause: you've been treating laziness like the villain of this story when it's actually the hero nobody thanked. Laziness isn't your enemy. It's your firewall — the thing blocking a DDoS attack of information you didn't ask for and don't need. A genuinely good engineer is a lazy one, and I mean that as the highest compliment I own. A good engineer can't be bothered learning something they won't use this afternoon. Healthy laziness says: &quot;I don't need Kubernetes until my monolith on one server actually starts falling over.&quot; Trying to learn it before that is like shoveling your driveway in the middle of a January blizzard, eh? Just pointless and cold, because five minutes later it's buried under a fresh layer of stuff you still don't need. You're allowed to look a shiny new technology dead in the eye and say &quot;I'm not learning you until you become my actual problem.&quot; That's not a character flaw. That's discipline with better PR. Think JIT compilation for your brain — compile the knowledge exactly when it's about to execute, don't sit there caching the entire internet in RAM on the off chance you'll need it in 2031.&lt;/p&gt;
&lt;p&gt;Once you've given yourself permission to be lazy about the right things, the next move is obvious, even if it feels wrong at first: just make it compile. YAGNI, applied to your actual life. Your only job while learning is to shrink the gap between &quot;I know nothing&quot; and &quot;it runs,&quot; as fast as humanly possible. Ugly is fine. Duct tape and hardcoded values are fine. Security holes the size of Jupiter — also, temporarily, fine. Ship the MVP. Get it running. Security, optimization, containers, clean architecture, all of that gets bolted on later, once you actually have context and a real need instead of a hypothetical one you invented to feel productive. You learn ten times faster from a live, bleeding error in production than from a book chapter preemptively warning you it might happen someday. That contact form from three paragraphs ago? Hardcode the email address. Skip the queue. Skip the retry logic. Ship it this afternoon, watch it break exactly once in some boring, specific way, and fix that one real problem instead of the fifty imaginary ones you were prepping for. Agile exists for a reason, and it's not just to give product managers something to say in standup. Write it working. Then make it pretty. Then, only then, make it fast.&lt;/p&gt;
&lt;p&gt;And once you're actually building things instead of pre-optimizing a brain that hasn't shipped anything yet, you hit the last real obstacle. It isn't technical at all. It's letting go of FOMO. The tech industry manufactures garbage faster than you can Google it. Trying to fully keep up is about as realistic as affording a detached house in Toronto on a junior dev salary — technically a number exists where it's possible, practically nobody you know has actually done it. You will never keep up, full stop, and the sooner you accept that, the sooner you stop feeling like a failure for it. Your real skill was never storage capacity. It's retrieval speed, and knowing what to throw out. Turn on your internal garbage collector. Start ruthlessly deallocating anything sitting in your head that isn't providing value right now. If you don't sweep unused objects out of memory, the whole system hangs — and your brain, despite what your CS degree might've implied, is not exempt from the laws of computer science it teaches.&lt;/p&gt;
&lt;p&gt;So, what do you think, eh? Or are you too busy writing YAML files for your zero-user startup to reply? Let me know which topics we should crack open next.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/24326/premature-optimization-of-your-brain-why-your-learning-roadmap-is-bad-architecture</guid>
<pubDate>Sun, 09 Aug 2026 10:17:36 +0000</pubDate>
</item>
<item>
<title>The Missing Semester of Your Tech Education: How to Stop Being a High-Level Puppet</title>
<link>https://coderlegion.com/24258/the-missing-semester-of-your-tech-education-how-to-stop-being-a-high-level-puppet</link>
<description>&lt;p&gt;Hey everyone. Been a minute, eh? Real talk — I went through a rough patch in my dev life the last while, the kind that makes you question whether you actually like this industry or just got really good at pretending. I'm back now, and I didn't want to show up empty-handed after ghosting you all like that, so here's a proper one. Grab your double-double, maybe a Timbit or six, this is a long-ish ride, but I promise it's a friendly one.&lt;/p&gt;
&lt;p&gt;And listen — before we get into the roasting, because there will be some roasting, I want to say this part sincerely: if you're reading this and you're early in your career, or mid-career and feeling behind, none of what follows is meant to make you feel small. It's meant the way a buddy tells you there's spinach in your teeth before the big meeting. Annoying for a second, genuinely useful after. We're all just figuring this out together, one Segfault at a time.&lt;/p&gt;
&lt;h3 id=&quot;episode-1-the-high-level-illusion&quot;&gt;Episode 1: The High-Level Illusion&lt;/h3&gt;
&lt;p&gt;Here's an uncomfortable little exercise. Ask a working web developer what an L1 cache line is. Ask them why a context switch costs anything at all. Ask them what a branch predictor actually predicts. Watch the face. That face is the whole problem.&lt;/p&gt;
&lt;p&gt;Modern languages didn't just make us productive, they made us spoiled. The garbage collector taught an entire generation to stop thinking about object lifetimes at all — memory just, sort of, handles itself, so why worry? You write &lt;code&gt;obj = JSON.parse(data)&lt;/code&gt; or slap an ORM on your queries and your brain files it under &quot;one line, basically free.&quot; Underneath that one line is a small riot of allocations, string parsing, and serialization overhead you never agreed to pay for, you just didn't get the bill yet.&lt;/p&gt;
&lt;p&gt;The processor became a black box on purpose. Nobody's malicious about it, that's just what abstraction is for. But you can't actually reason about why your &quot;high-performance&quot; microservice chokes under load if the hardware it runs on is a total mystery to you. You're debugging a car you've never opened the hood of, and somehow surprised when &quot;just add more RAM&quot; stops working.&lt;/p&gt;
&lt;h3 id=&quot;episode-2-the-bare-metal-baptism&quot;&gt;Episode 2: The Bare Metal Baptism&lt;/h3&gt;
&lt;p&gt;There's exactly one cure for high-level brain rot, and it's unpleasant on purpose: go write something in C. No libraries holding your hand. No pre-built hash map, no pre-built linked list. Build your own, badly, then build it again less badly.&lt;/p&gt;
&lt;p&gt;You will hit Segfault #1. Then #2. Then somewhere around #50 you'll start actually understanding what memory looks like instead of just trusting that it works. &lt;code&gt;Valgrind&lt;/code&gt; will roast you for leaks you didn't know you were capable of creating. That pain is doing something — it's wiring an actual, physical map of memory into your brain, the kind no tutorial video gives you secondhand.&lt;/p&gt;
&lt;p&gt;Once you've fought with raw pointers and manual &lt;code&gt;malloc&lt;/code&gt;/&lt;code&gt;free&lt;/code&gt; for a while, you start understanding things you couldn't have been talked into believing otherwise: why copying big structs in a hot loop is a crime, why the stack is faster than the heap and why that actually matters, why &quot;it's fine, the language handles it&quot; was never really true, just deferred. This knowledge doesn't stay in C. It follows you into Python, into Java, into whatever you write for the rest of your career, and it makes you dangerous in the good way.&lt;/p&gt;
&lt;p&gt;And hey — if you're mid-Segfault-thirty right now, cursing my name, that's normal. Everyone who's good at this went through the exact same ugly stretch. Nobody was born understanding pointers, we all just eventually got tired of being confused and pushed through it, eh.&lt;/p&gt;
&lt;h3 id=&quot;episode-3-literacy-of-the-masters&quot;&gt;Episode 3: Literacy of the Masters&lt;/h3&gt;
&lt;p&gt;Writing your own janky code in total isolation is a dead end, and I say that with love. At some point you have to go read the people who actually built this industry, or you'll spend your whole career reinventing worse versions of things that already exist.&lt;/p&gt;
&lt;p&gt;Juniors write endless little wheels off dumb tutorials because they've genuinely never seen what beautiful, battle-tested code looks like. So go look. Crack open &lt;strong&gt;Redis&lt;/strong&gt; — genuinely gorgeous, minimal C. Read &lt;strong&gt;SQLite&lt;/strong&gt;. Read &lt;strong&gt;nginx&lt;/strong&gt; if you're feeling brave. These aren't textbooks written by theorists trying to teach you a concept. This is code holding up terabytes of real traffic, written by people who had to get it right.&lt;/p&gt;
&lt;p&gt;Don't try to swallow the whole thing at once, that's a losing game. Find the entry point — &lt;code&gt;main&lt;/code&gt;, usually — and figure out the one core data structure the whole thing revolves around, like Redis's &lt;code&gt;sds&lt;/code&gt; string implementation. Watch how the authors handle errors. Watch how they manage memory without losing their minds. That's the actual architecture school nobody enrolled you in.&lt;/p&gt;
&lt;h3 id=&quot;episode-4-the-cloud-scam-and-bare-metal-reality&quot;&gt;Episode 4: The Cloud Scam and Bare Metal Reality&lt;/h3&gt;
&lt;p&gt;Somewhere along the way, the industry forgot how to optimize software and started just throwing investor money at problems instead. That's the honest version of the story nobody puts in the pitch deck.&lt;/p&gt;
&lt;p&gt;You get told you need Kubernetes, Lambda, full serverless, and twenty VMs to scale a site pulling ten thousand visitors. For most of you reading this, that's not engineering, that's a scam wearing a hoodie. A properly written, async, actually-optimized service on a single fifty-dollar bare metal box can push tens of thousands of requests per second at single-digit-millisecond latency. No cluster required. No YAML novel required.&lt;/p&gt;
&lt;p&gt;Before you touch Kubernetes, you should be able to SSH into a server, configure &lt;code&gt;iptables&lt;/code&gt;/&lt;code&gt;ufw&lt;/code&gt; without googling every flag, understand what &lt;code&gt;systemd&lt;/code&gt; is actually doing when it starts your service, and hand-edit an nginx config without breaking production. If you can't administer one server calmly, adding nine more of them isn't going to fix that, it's just going to multiply the panic.&lt;/p&gt;
&lt;h3 id=&quot;episode-5-the-ai-autocomplete-trap&quot;&gt;Episode 5: The AI Autocomplete Trap&lt;/h3&gt;
&lt;p&gt;Okay, this one's the elephant in the room, and I'd be a phony writing a whole post about high-level illusions in 2026 and not mentioning the biggest one currently happening live, in real time, to basically all of us.&lt;/p&gt;
&lt;p&gt;AI coding tools are genuinely great, I'm not here to be the grumpy guy yelling at the cloud, no shade to actual clouds either after Episode 4. Copilot, Cursor, whatever you're using — they're a real productivity boost, and pretending otherwise is just cope. But here's the quiet, uncomfortable thing happening underneath the convenience: we just added a whole new abstraction layer on top of all the other abstraction layers, and this one doesn't even ask you to understand the code it hands you. It just hands it to you, confident, formatted nicely, and you tab-complete your way past actually reading it.&lt;/p&gt;
&lt;p&gt;That's the new version of the same old sin from Episode 1, just wearing a nicer jacket. Before, you didn't understand what the garbage collector was doing. Now you might not even understand what the function you just accepted does, because you never typed it, never fought with it, never had the &quot;wait, why is this null&quot; moment that actually teaches you anything. The suggestion just... appeared, looked plausible, compiled, and you moved on. That's not learning, that's outsourcing your own competence to autocomplete and hoping nobody asks you to explain it in a code review.&lt;/p&gt;
&lt;p&gt;Here's the honest, non-doom-and-gloom version of what to actually do about it: use the tools, for sure, they're not going anywhere and fighting that is a losing battle. But treat every AI suggestion like a pull request from a very fast, very confident junior dev who's never met your codebase. Read it. Question it. Ask yourself if you could've written that yourself, or explained why it's wrong if it is wrong. The moment you stop being able to tell the difference between &quot;I understand this&quot; and &quot;this looks right,&quot; you've quietly become the high-level puppet from Episode 1 again, just with extra steps and a monthly subscription.&lt;/p&gt;
&lt;h3 id=&quot;episode-6-a-few-honest-tips-for-not-losing-your-mind-in-this-industry&quot;&gt;Episode 6: A Few Honest Tips for Not Losing Your Mind in This Industry&lt;/h3&gt;
&lt;p&gt;Alright, less roasting now, more actual advice, because I do genuinely want you to do well out there, eh.&lt;/p&gt;
&lt;p&gt;Learn one thing below your comfort zone every few months. Doesn't have to be a whole language — read one systems paper, build one tiny project in C, sit through one lecture on how TCP actually handshakes. Small, consistent trips downward keep the muscle alive.&lt;/p&gt;
&lt;p&gt;Ask better questions before you ask more questions. Ten minutes with the actual docs saves you two days of guessing, and it makes the people who could help you actually want to.&lt;/p&gt;
&lt;p&gt;Don't chase every shiny framework that trends for a week. Half of them are gone in a year, the fundamentals under them never expire. Being the person who still knows how HTTP actually works in five years is worth more than being first to try whatever's hot this month.&lt;/p&gt;
&lt;p&gt;Find your people. A good, slightly grumpy senior who'll actually review your code properly is worth more than a hundred YouTube tutorials. Buy them a coffee sometime, seriously, it goes further than you'd think.&lt;/p&gt;
&lt;p&gt;And take care of yourself while you're at it. I wasn't kidding about the rough patch earlier — burnout is real, it sneaks up quiet, and no amount of clever pointer arithmetic fixes a person who's running on empty. Rest counts as progress too, don't let anyone tell you otherwise.&lt;/p&gt;
&lt;h3 id=&quot;episode-7-the-code-of-conduct-in-real-communities&quot;&gt;Episode 7: The Code of Conduct in Real Communities&lt;/h3&gt;
&lt;p&gt;Last one, and it's less technical, more about how you survive rooms full of people smarter than you, which — good news — is most rooms if you're doing this right.&lt;/p&gt;
&lt;p&gt;Old-timers in strong communities have a reputation for being a little mean, and honestly, most of the time it's earned mean, not random mean. Show up with &quot;my script doesn't work, help&quot; and zero effort behind it, and you'll get ignored or roasted, and that's actually correct behaviour from them, not cruelty.&lt;/p&gt;
&lt;p&gt;A good question respects the other person's time, because you respect your own. That means: the actual context and what you're trying to accomplish, the exact error log in text — not a phone screenshot of a monitor, we can all see the reflection of your ceiling fan — and a real list of what you've already tried and why it didn't work.&lt;/p&gt;
&lt;p&gt;Show up having actually opened the docs, actually narrowed down the problem, and gotten stuck on something genuinely interesting — and people will help you with real enthusiasm, sometimes more than you expected. That's not luck. That's just how real engineering relationships get built, one well-asked question at a time.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Anyway. Good to be back, eh. This industry's a weird one — it'll humble you, ghost you, and occasionally make you cry over a missing semicolon at 2am, but it's also full of genuinely good people who'll help you out for nothing more than a well-asked question and a bit of respect for their time. You're doing better than you think you are. Go build something, break it, learn why, and I'll catch you in the next one. Take care of yourselves out there, for real.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/24258/the-missing-semester-of-your-tech-education-how-to-stop-being-a-high-level-puppet</guid>
<pubDate>Fri, 07 Aug 2026 11:15:42 +0000</pubDate>
</item>
<item>
<title>Reinvention Is a Practice: Staying Open to What Comes Next</title>
<link>https://coderlegion.com/24213/reinvention-is-a-practice-staying-open-to-what-comes-next</link>
<description>&lt;p&gt;Lately, I have been thinking about change.&lt;/p&gt;
&lt;p&gt;Not the dramatic kind where you wake up one morning, abandon your entire life and announce a new identity to the world. Rather, the gradual process of recognising that a version of you has served its purpose, and that it may be time to become someone slightly different.&lt;/p&gt;
&lt;p&gt;When I look back at my life, I can see several versions of myself.&lt;/p&gt;
&lt;p&gt;There was the young developer learning how to code, spending long hours trying to understand why things worked the way they did. There was the engineer wrestling with Docker until containers, networks and deployment pipelines finally stopped feeling like dark magic. There was the blockchain developer learning an entirely new ecosystem during the NFT wave. Now there is the engineer and business owner trying to understand what it means to build in the age of AI.&lt;/p&gt;
&lt;p&gt;Each version required something different from me.&lt;/p&gt;
&lt;p&gt;New skills. New habits. New ways of thinking.&lt;/p&gt;
&lt;p&gt;More importantly, each one required me to accept that what had brought me this far might not be enough to carry me forward.&lt;/p&gt;
&lt;p&gt;That is perhaps the most uncomfortable part of reinvention.&lt;/p&gt;
&lt;h3 id=&quot;the-humility-of-becoming-a-beginner-again&quot;&gt;The Humility of Becoming a Beginner Again&lt;/h3&gt;
&lt;p&gt;There is a particular kind of discomfort that comes with learning something new.&lt;/p&gt;
&lt;p&gt;You go from feeling competent to feeling completely lost. Concepts that other people discuss casually sound foreign. Simple tasks take hours. Progress is difficult to measure, and there are moments when you wonder whether the effort is worth it.&lt;/p&gt;
&lt;p&gt;I remember the long hours I spent learning Docker.&lt;/p&gt;
&lt;p&gt;At the time, it did not feel like some grand career investment. It mostly felt like confusion, poorly written configuration files and an unreasonable number of browser tabs. There were evenings when I understood less at the end than I thought I had understood at the beginning.&lt;/p&gt;
&lt;p&gt;Eventually, things clicked.&lt;/p&gt;
&lt;p&gt;That knowledge helped me thrive in later roles. It made me more useful during infrastructure changes and better equipped to understand the systems around the software I was building. It gave me the confidence to move beyond writing code and think more deeply about how software is deployed, operated and maintained.&lt;/p&gt;
&lt;p&gt;The effort repaid itself many times over.&lt;/p&gt;
&lt;p&gt;That has been true of most valuable things I have learned. The rewards rarely arrive at the same time as the discomfort. At first, you only experience the cost. The late nights, the confusion and the repeated failures are immediate. The benefits reveal themselves later, often in situations you could not have predicted.&lt;/p&gt;
&lt;p&gt;Knowledge compounds quietly.&lt;/p&gt;
&lt;p&gt;One skill makes the next one easier to learn. A difficult project gives you pattern recognition for the next challenge. A season of confusion becomes the foundation for decisions that later appear instinctive.&lt;/p&gt;
&lt;p&gt;From the outside, that can look like talent.&lt;/p&gt;
&lt;p&gt;From the inside, it feels more like accumulated struggle.&lt;/p&gt;
&lt;h3 id=&quot;learning-to-move-with-the-world&quot;&gt;Learning to Move With the World&lt;/h3&gt;
&lt;p&gt;Technology has given me little choice but to become comfortable with change.&lt;/p&gt;
&lt;p&gt;When blockchain and NFTs became a major frontier, I had to learn how that world worked. It meant understanding new tools, architectures and assumptions about ownership, payments and digital assets.&lt;/p&gt;
&lt;p&gt;That wave eventually cooled, as waves tend to do.&lt;/p&gt;
&lt;p&gt;But the knowledge did not disappear with the hype. The experience broadened how I understood financial systems and emerging technology. It also taught me something more important: I could enter an unfamiliar space, endure the confusion and eventually become competent within it.&lt;/p&gt;
&lt;p&gt;Now we are in the AI era, and I find myself adapting again.&lt;/p&gt;
&lt;p&gt;The tools are changing quickly. The way software is written is changing. Work that once took days can sometimes be completed in hours, but that speed introduces new questions around judgment, quality and responsibility.&lt;/p&gt;
&lt;p&gt;I could resist it. I could insist that the methods that worked for me before should continue working forever.&lt;/p&gt;
&lt;p&gt;But the world is under no obligation to preserve the conditions under which I became successful.&lt;/p&gt;
&lt;p&gt;Standing still does not protect you from change. It only removes your ability to influence how change affects you.&lt;/p&gt;
&lt;p&gt;I have noticed that some of my older and more successful colleagues are remarkably open to new ideas. They do not respond to every development with insecurity or dismissal. They ask questions. They test things. They are willing to admit that someone younger or less experienced may understand an emerging field better than they do.&lt;/p&gt;
&lt;p&gt;That openness seems to be a common thread among many of the successful people I know.&lt;/p&gt;
&lt;p&gt;They do not confuse experience with completion.&lt;/p&gt;
&lt;p&gt;Being good at something today does not exempt you from learning tomorrow.&lt;/p&gt;
&lt;h3 id=&quot;firing-myself-from-the-work-i-know&quot;&gt;Firing Myself From the Work I Know&lt;/h3&gt;
&lt;p&gt;Running a business has introduced a different kind of reinvention.&lt;/p&gt;
&lt;p&gt;As a developer, my instinct is to solve problems by building things. When something needs to be fixed, I want to open my laptop, inspect the code and handle it myself. Coding is familiar. It gives me a sense of control and the satisfaction of knowing that I can personally move something forward.&lt;/p&gt;
&lt;p&gt;But a business cannot grow around the founder’s need to remain involved in everything.&lt;/p&gt;
&lt;p&gt;Someone once advised me to fire myself every day.&lt;/p&gt;
&lt;p&gt;The first time I heard it, it sounded slightly aggressive. Over time, I began to understand the wisdom in it.&lt;/p&gt;
&lt;p&gt;The goal is not to make yourself useless. It is to stop being the permanent bottleneck.&lt;/p&gt;
&lt;p&gt;Every time I build a system, document a process, delegate responsibility or allow someone else to take ownership, I am firing myself from a role the business should no longer depend on me to perform.&lt;/p&gt;
&lt;p&gt;That sounds simple in theory. In practice, it can be deeply uncomfortable.&lt;/p&gt;
&lt;p&gt;It is difficult to release work you know you can do well. Delegation requires trust. It requires patience when someone approaches a problem differently from how you would. Sometimes it means accepting that another person may eventually become better at that work than you are.&lt;/p&gt;
&lt;p&gt;Holding on to every task also comes at a cost.&lt;/p&gt;
&lt;p&gt;The more time I spend coding every feature, reviewing every small decision and solving every immediate problem, the less time I have to think about where the business is going.&lt;/p&gt;
&lt;p&gt;My role has to evolve.&lt;/p&gt;
&lt;p&gt;I have to think about strategy, partnerships, positioning, sales and the kind of company we are trying to become. These responsibilities do not always offer the immediate satisfaction of shipping a feature, but they are increasingly where I can create the most value.&lt;/p&gt;
&lt;p&gt;Growth sometimes requires leaving behind the work that once made you feel valuable.&lt;/p&gt;
&lt;p&gt;That is its own form of reinvention.&lt;/p&gt;
&lt;h3 id=&quot;letting-go-outside-of-work&quot;&gt;Letting Go Outside of Work&lt;/h3&gt;
&lt;p&gt;Change is not only necessary in careers and businesses.&lt;/p&gt;
&lt;p&gt;Sometimes the habits that need to be reconsidered are personal.&lt;/p&gt;
&lt;p&gt;There was a time when almost every weekend needed to involve going out. There was always somewhere to be, someone to meet or another loud room to enter. It was fun, until parts of it stopped being fun and continued mostly because that was what weekends had come to mean.&lt;/p&gt;
&lt;p&gt;Lately, I have been embracing a quieter life.&lt;/p&gt;
&lt;p&gt;For now, at least.&lt;/p&gt;
&lt;p&gt;I am learning to enjoy my own company without treating solitude as an empty space that needs to be filled. I am becoming more intentional about how I spend my time and which habits genuinely leave me feeling restored.&lt;/p&gt;
&lt;p&gt;This has not meant withdrawing from life. In some ways, it has created space for different experiences.&lt;/p&gt;
&lt;p&gt;During a recent solo trip to Diani, I decided to try kitesurfing.&lt;/p&gt;
&lt;p&gt;There is something humbling about picking up a new sport as an adult. You do not have the luxury of pretending to be naturally competent. The wind does not care about your career, confidence or carefully constructed sense of dignity. You fall, struggle, get up and try again.&lt;/p&gt;
&lt;p&gt;I enjoyed it.&lt;/p&gt;
&lt;p&gt;Not because I was immediately good at it, but because I was not.&lt;/p&gt;
&lt;p&gt;It reminded me of the pleasure of entering an experience without needing to perform expertise. Of allowing curiosity, rather than competence, to lead.&lt;/p&gt;
&lt;p&gt;I would like to become better at it over time. Not urgently, and not because every hobby must become a measurable achievement. Simply because there is something meaningful about giving yourself new worlds to grow into.&lt;/p&gt;
&lt;h3 id=&quot;discomfort-is-not-always-a-warning&quot;&gt;Discomfort Is Not Always a Warning&lt;/h3&gt;
&lt;p&gt;We often interpret discomfort as evidence that something is wrong.&lt;/p&gt;
&lt;p&gt;Sometimes it is. There are situations, relationships and environments that should be left rather than endured.&lt;/p&gt;
&lt;p&gt;But discomfort can also mean that an old identity is being stretched.&lt;/p&gt;
&lt;p&gt;Learning exposes what we do not know. Delegation reduces our control. A quieter life feels unfamiliar when we have grown accustomed to noise. Starting a new sport reminds us that adulthood offers few opportunities to be visibly bad at something.&lt;/p&gt;
&lt;p&gt;The instinct is often to retreat into what is familiar.&lt;/p&gt;
&lt;p&gt;Familiarity, however, can become a very comfortable cage.&lt;/p&gt;
&lt;p&gt;Some of the most meaningful changes in my life began with periods I would not have chosen for myself. They required uncertainty, patience and the temporary loss of confidence that comes with moving beyond what you already understand.&lt;/p&gt;
&lt;p&gt;Looking back, I do not regret the discomfort.&lt;/p&gt;
&lt;p&gt;I regret some of the moments when fear made me delay it.&lt;/p&gt;
&lt;h3 id=&quot;a-letter-to-the-version-of-me-who-feels-stuck&quot;&gt;A Letter to the Version of Me Who Feels Stuck&lt;/h3&gt;
&lt;p&gt;Perhaps this is partly a letter to myself.&lt;/p&gt;
&lt;p&gt;A reminder to return to when motivation is low, the future feels uncertain or the work of becoming someone new feels heavier than remaining where I am.&lt;/p&gt;
&lt;p&gt;I want to remember that I have done this before.&lt;/p&gt;
&lt;p&gt;I learned to code when I did not know how.&lt;/p&gt;
&lt;p&gt;I learned infrastructure when the concepts felt impossibly complicated.&lt;/p&gt;
&lt;p&gt;I entered blockchain when the ecosystem was unfamiliar.&lt;/p&gt;
&lt;p&gt;I am learning to build differently in the age of AI.&lt;/p&gt;
&lt;p&gt;I am learning to lead a business rather than simply work inside one.&lt;/p&gt;
&lt;p&gt;I am learning to release habits, roles and versions of myself that no longer serve the person I am trying to become.&lt;/p&gt;
&lt;p&gt;None of those transitions has been perfectly planned.&lt;/p&gt;
&lt;p&gt;Most began with a mixture of curiosity, necessity and discomfort. The confidence usually arrived after I started, not before.&lt;/p&gt;
&lt;p&gt;That may be the lesson worth keeping.&lt;/p&gt;
&lt;p&gt;You do not need to feel fully prepared for the next version of your life. You only need enough humility to admit that change is necessary and enough courage to take the next step.&lt;/p&gt;
&lt;p&gt;Reinvention does not always require a dramatic announcement.&lt;/p&gt;
&lt;p&gt;Sometimes it is learning a new tool after work.&lt;/p&gt;
&lt;p&gt;Sometimes it is trusting someone else with a responsibility you once carried alone.&lt;/p&gt;
&lt;p&gt;Sometimes it is declining another loud weekend and choosing rest.&lt;/p&gt;
&lt;p&gt;Sometimes it is standing on a beach in Diani, attached to a kite, being dragged through the water while discovering that you are still capable of surprising yourself.&lt;/p&gt;
&lt;p&gt;The world will continue changing.&lt;/p&gt;
&lt;p&gt;So will the work.&lt;/p&gt;
&lt;p&gt;So will the people around us.&lt;/p&gt;
&lt;p&gt;The question is not whether change will arrive. It is whether we will remain open enough to move with it.&lt;/p&gt;
&lt;p&gt;I hope I do.&lt;/p&gt;
&lt;p&gt;I hope I remain curious enough to explore new frontiers, disciplined enough to endure the uncomfortable parts and humble enough to become a beginner as many times as life requires.&lt;/p&gt;
&lt;p&gt;Because perhaps reinvention is not about becoming an entirely different person.&lt;/p&gt;
&lt;p&gt;Perhaps it is about refusing to believe that the person you are today is the only person you are capable of becoming.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24213/reinvention-is-a-practice-staying-open-to-what-comes-next</guid>
<pubDate>Thu, 06 Aug 2026 12:17:46 +0000</pubDate>
</item>
<item>
<title>GraphQL vs REST - Which One Should You Really Use?</title>
<link>https://coderlegion.com/24103/graphql-vs-rest-which-one-should-you-really-use</link>
<description>&lt;p&gt;For years, REST APIs have been the standard.&lt;/p&gt;
&lt;p&gt;Then GraphQL came in with promises like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Fetch exactly what you need”&lt;/li&gt;
&lt;li&gt;“Reduce API calls”&lt;/li&gt;
&lt;li&gt;“More flexible APIs”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But here’s the reality:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;GraphQL is not a replacement for REST.&lt;br&gt;
It’s a different trade-off.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let’s break it down — with &lt;strong&gt;real code you can run&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-what-is-rest-&quot;&gt;🧠 What is REST?&lt;/h2&gt;
&lt;p&gt;REST is a resource-based API design.&lt;/p&gt;
&lt;p&gt;You expose endpoints like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;GET /users/1
GET /users/1/orders
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each endpoint returns &lt;strong&gt;fixed structure data&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-rest-api-fully-working-go-example&quot;&gt;⚙️ REST API — Fully Working Go Example&lt;/h2&gt;
&lt;p&gt;👉 Save as &lt;code&gt;rest.go&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;package main

import (
&quot;encoding/json&quot;
&quot;log&quot;
&quot;net/http&quot;
)

type User struct {
ID    int    `json:&quot;id&quot;`
Name  string `json:&quot;name&quot;`
Email string `json:&quot;email&quot;`
}

func getUserHandler(w http.ResponseWriter, r *http.Request) {
user := User{
ID:    1,
Name:  &quot;John&quot;,
Email: &quot;*Emails are not allowed*&quot;,
}

w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)
json.NewEncoder(w).Encode(user)
}

func main() {
http.HandleFunc(&quot;/users/1&quot;, getUserHandler)

log.Println(&quot;REST server running on http://localhost:8080&quot;)
log.Fatal(http.ListenAndServe(&quot;:8080&quot;, nil))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-run-it&quot;&gt;▶️ Run it&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;go run rest.go
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-call-api&quot;&gt;📡 Call API&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl http://localhost:8080/users/1
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-response&quot;&gt;📦 Response&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;id&quot;: 1,
  &quot;name&quot;: &quot;John&quot;,
  &quot;email&quot;: &quot;*Emails are not allowed*&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-what-is-graphql-&quot;&gt;🧠 What is GraphQL?&lt;/h2&gt;
&lt;p&gt;GraphQL is a &lt;strong&gt;query-based API system&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Instead of multiple endpoints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You use a &lt;strong&gt;single endpoint&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Client decides what data it needs&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-graphql-api-fully-working-go-example&quot;&gt;⚙️ GraphQL API — Fully Working Go Example&lt;/h2&gt;
&lt;p&gt;👉 Save as &lt;code&gt;graphql.go&lt;/code&gt;&lt;/p&gt;
&lt;h4 id=&quot;step-1-install-dependency&quot;&gt;Step 1: Install dependency&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;go mod init graphql-example
go get github.com/graphql-go/graphql
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;step-2-full-working-code&quot;&gt;Step 2: Full working code&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-go&quot;&gt;package main

import (
&quot;encoding/json&quot;
&quot;log&quot;
&quot;net/http&quot;

&quot;github.com/graphql-go/graphql&quot;
)

type User struct {
ID    int    `json:&quot;id&quot;`
Name  string `json:&quot;name&quot;`
Email string `json:&quot;email&quot;`
}

var userData = User{
ID:    1,
Name:  &quot;John&quot;,
Email: &quot;*Emails are not allowed*&quot;,
}

func main() {

// Define User type
userType := graphql.NewObject(graphql.ObjectConfig{
Name: &quot;User&quot;,
Fields: graphql.Fields{
&quot;id&quot;: &amp;amp;graphql.Field{
Type: graphql.Int,
},
&quot;name&quot;: &amp;amp;graphql.Field{
Type: graphql.String,
},
&quot;email&quot;: &amp;amp;graphql.Field{
Type: graphql.String,
},
},
})

// Root query
rootQuery := graphql.NewObject(graphql.ObjectConfig{
Name: &quot;Query&quot;,
Fields: graphql.Fields{
&quot;user&quot;: &amp;amp;graphql.Field{
Type: userType,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return userData, nil
},
},
},
})

schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: rootQuery,
})
if err != nil {
log.Fatal(err)
}

// GraphQL handler
http.HandleFunc(&quot;/graphql&quot;, func(w http.ResponseWriter, r *http.Request) {
var params struct {
Query string `json:&quot;query&quot;`
}

err := json.NewDecoder(r.Body).Decode(&amp;amp;params)
if err != nil {
http.Error(w, &quot;invalid request body&quot;, http.StatusBadRequest)
return
}

result := graphql.Do(graphql.Params{
Schema:        schema,
RequestString: params.Query,
})

w.Header().Set(&quot;Content-Type&quot;, &quot;application/json&quot;)
json.NewEncoder(w).Encode(result)
})

log.Println(&quot;GraphQL server running on http://localhost:8080/graphql&quot;)
log.Fatal(http.ListenAndServe(&quot;:8080&quot;, nil))
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-run-it-1&quot;&gt;▶️ Run it&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;go run graphql.go
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-call-api-1&quot;&gt;📡 Call API&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -X POST http://localhost:8080/graphql \
-H &quot;Content-Type: application/json&quot; \
-d '{&quot;query&quot;: &quot;{ user { name email } }&quot;}'
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-response-1&quot;&gt;📦 Response&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;data&quot;: {
    &quot;user&quot;: {
      &quot;name&quot;: &quot;John&quot;,
      &quot;email&quot;: &quot;*Emails are not allowed*&quot;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;rest-vs-graphql-visual-understanding&quot;&gt;REST vs GraphQL — Visual Understanding&lt;/h2&gt;
&lt;h3 id=&quot;rest-flow&quot;&gt;REST Flow&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Client
  |
  |---- GET /users/1 ----------&amp;gt; Server
  |---- GET /users/1/orders ---&amp;gt; Server
  |---- GET /orders/101/items -&amp;gt; Server

Multiple requests ❌
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;graphql-flow&quot;&gt;GraphQL Flow&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphql&quot;&gt;Client
  |
  |---- POST /graphql ---------&amp;gt; Server
        query {
          user {
            orders {
              items
            }
          }
        }

Single request ✅
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-the-real-problem-graphql-solves&quot;&gt;⚠️ The Real Problem GraphQL Solves&lt;/h2&gt;
&lt;h3 id=&quot;rest-problem-over-fetching&quot;&gt;REST Problem → Over-fetching&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;GET /users/1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Returns:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-csvs&quot;&gt;name, email, address, phone...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even if you need only &lt;code&gt;name&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;rest-problem-under-fetching&quot;&gt;REST Problem → Under-fetching&lt;/h3&gt;
&lt;p&gt;You need multiple calls to build UI.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;graphql-solution&quot;&gt;GraphQL Solution&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphql&quot;&gt;query {
  user {
    name
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;👉 Exact data only.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-rest-vs-graphql-honest-comparison&quot;&gt;⚖️ REST vs GraphQL — Honest Comparison&lt;/h2&gt;
&lt;h3 id=&quot;-rest&quot;&gt;🧩 REST&lt;/h3&gt;
&lt;p&gt;✅ Simple&lt;br&gt;
✅ Easy caching&lt;br&gt;
✅ Easy debugging&lt;br&gt;
❌ Multiple calls&lt;br&gt;
❌ Over-fetching&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-graphql&quot;&gt;🧩 GraphQL&lt;/h3&gt;
&lt;p&gt;✅ Flexible&lt;br&gt;
✅ Single request&lt;br&gt;
❌ Complex&lt;br&gt;
❌ Hard caching&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Note: REST uses native HTTP caching (like CDNs and browser headers), while GraphQL requires complex, custom caching strategies because queries are typically sent via POST.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;❌ Query abuse risk&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-common-mistakes&quot;&gt;⚠️ Common Mistakes&lt;/h2&gt;
&lt;h4 id=&quot;-graphql-is-always-better-&quot;&gt;❌ “GraphQL is always better”&lt;/h4&gt;
&lt;p&gt;No.&lt;/p&gt;
&lt;h4 id=&quot;-using-graphql-for-simple-crud&quot;&gt;❌ Using GraphQL for simple CRUD&lt;/h4&gt;
&lt;p&gt;Overkill.&lt;/p&gt;
&lt;h4 id=&quot;-ignoring-query-complexity&quot;&gt;❌ Ignoring query complexity&lt;/h4&gt;
&lt;p&gt;Bad query:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;users → posts → comments → replies → ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;💥 Performance issue&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-when-to-use-what&quot;&gt;🧠 When to Use What&lt;/h2&gt;
&lt;h3 id=&quot;-use-rest-when-&quot;&gt;✅ Use REST when:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Simple APIs&lt;/li&gt;
&lt;li&gt;CRUD operations&lt;/li&gt;
&lt;li&gt;Strong caching needed&lt;/li&gt;
&lt;li&gt;Easy debugging required&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-use-graphql-when-&quot;&gt;✅ Use GraphQL when:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Complex frontend&lt;/li&gt;
&lt;li&gt;Aggregated data&lt;/li&gt;
&lt;li&gt;Multiple services&lt;/li&gt;
&lt;li&gt;Frequent UI changes&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-real-world-architecture&quot;&gt;🔗 Real-World Architecture&lt;/h2&gt;
&lt;p&gt;Many companies use the GraphQL Gateway (or BFF) pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Frontend → GraphQL Gateway → REST microservices
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;👉 GraphQL acts as an aggregation layer, allowing you to combine multiple REST responses into one, without refactoring the legacy backend.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-final-thoughts-2&quot;&gt;🏁 Final Thoughts&lt;/h2&gt;
&lt;p&gt;GraphQL vs REST is not a battle.&lt;/p&gt;
&lt;p&gt;It’s a &lt;strong&gt;design decision&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-key-takeaways-&quot;&gt;🎯 Key Takeaways:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;REST = simple and predictable&lt;/li&gt;
&lt;li&gt;GraphQL = flexible but complex&lt;/li&gt;
&lt;li&gt;Both solve real problems&lt;/li&gt;
&lt;li&gt;Choose based on use-case&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;-final-tip&quot;&gt;🚀 Final Tip&lt;/h2&gt;
&lt;p&gt;If your API:&lt;/p&gt;
&lt;p&gt;👉 Is simple → use REST&lt;br&gt;
👉 Needs flexibility → use GraphQL&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24103/graphql-vs-rest-which-one-should-you-really-use</guid>
<pubDate>Tue, 04 Aug 2026 17:28:59 +0000</pubDate>
</item>
<item>
<title>🛡️ Content Security Policy (CSP): A Complete Guide for Developers</title>
<link>https://coderlegion.com/23772/content-security-policy-csp-a-complete-guide-for-developers</link>
<description>&lt;p&gt;Security is one of the most critical aspects of modern web development. Even if your code is correct, attackers may exploit vulnerabilities like &lt;strong&gt;Cross-Site Scripting (XSS)&lt;/strong&gt; or &lt;strong&gt;Clickjacking&lt;/strong&gt;. That’s where &lt;strong&gt;Content Security Policy (CSP)&lt;/strong&gt; comes in.&lt;/p&gt;
&lt;p&gt;This blog covers:&lt;br&gt;
✅ What CSP is &amp;amp; how it works&lt;br&gt;
✅ All major directives (&lt;code&gt;default-src&lt;/code&gt;, &lt;code&gt;script-src&lt;/code&gt;, &lt;code&gt;img-src&lt;/code&gt;, etc.) explained in detail&lt;br&gt;
✅ Real-world examples&lt;br&gt;
✅ Best practices &amp;amp; common pitfalls&lt;/p&gt;
&lt;p&gt;Let’s dive in. 🔥&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-is-csp-amp-why-do-we-need-it-&quot;&gt;⚙️ What is CSP &amp;amp; Why Do We Need It?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Content Security Policy (CSP) is an &lt;strong&gt;HTTP response header&lt;/strong&gt; that defines where browsers can load resources (scripts, images, styles, fonts, iframes, workers, etc.) from.&lt;/li&gt;
&lt;li&gt;Its main purpose: &lt;strong&gt;prevent XSS attacks&lt;/strong&gt; by restricting execution of malicious scripts.&lt;/li&gt;
&lt;li&gt;Without CSP, if an attacker injects a script into your page, the browser executes it blindly. With CSP, you can &lt;strong&gt;whitelist only trusted domains&lt;/strong&gt;, blocking everything else.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example CSP header:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: default-src 'self'; img-src https://cdn.example.com; script-src 'self' https://apis.google.com;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;By default, only load resources from the same origin (&lt;code&gt;'self'&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Allow images from &lt;code&gt;cdn.example.com&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Allow scripts from self and Google APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;⚠️ &lt;strong&gt;Important:&lt;/strong&gt;&lt;br&gt;
 If &lt;code&gt;default-src&lt;/code&gt; is missing, everything is allowed, weakening your CSP.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-important-directives&quot;&gt;📌 Important Directives&lt;/h3&gt;
&lt;p&gt;Here’s a breakdown of &lt;strong&gt;commonly used CSP directives&lt;/strong&gt;:&lt;/p&gt;
&lt;h4 id=&quot;-default-src&quot;&gt;🔑 &lt;code&gt;default-src&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;The fallback for all other directives (if they are not explicitly defined).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Content-Security-Policy: default-src 'self';
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;script-src&lt;/code&gt; or &lt;code&gt;style-src&lt;/code&gt; is missing, browsers fall back to &lt;code&gt;default-src&lt;/code&gt;. ✅&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-script-src&quot;&gt;📜 &lt;code&gt;script-src&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Defines where JavaScript can be loaded from.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Content-Security-Policy: script-src 'self' https://apis.google.com;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Special values:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;'self'&lt;/code&gt;: allow only current domain&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'unsafe-eval'&lt;/code&gt;: allow &lt;code&gt;eval()&lt;/code&gt; usage (⚠️ dangerous)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;'unsafe-inline'&lt;/code&gt;: allow inline scripts (⚠️ not recommended)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Better options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nonce&lt;/strong&gt; → Random value per request:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script nonce=&quot;abc123&quot;&amp;gt;console.log(&quot;Safe!&quot;);&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With CSP:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Content-Security-Policy: script-src 'self' 'nonce-abc123';
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hash&lt;/strong&gt; → Allow only exact inline script:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script&amp;gt;alert(&quot;hello&quot;)&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With CSP:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: script-src 'self' 'sha256-XYZhashHere...';
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ Nonce &amp;amp; Hash are safer than &lt;code&gt;'unsafe-inline'&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-style-src&quot;&gt;🎨 &lt;code&gt;style-src&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Controls where CSS can be loaded from.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: style-src 'self' https://fonts.googleapis.com;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⚠️ Using &lt;code&gt;'unsafe-inline'&lt;/code&gt; allows inline styles → risky.&lt;br&gt;
🔑 Instead:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;nonce&lt;/strong&gt; or &lt;strong&gt;hashes&lt;/strong&gt; for dynamic styles.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;style nonce=&quot;abc123&quot;&amp;gt;.btn { color: red; }&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-img-src&quot;&gt;🖼️ &lt;code&gt;img-src&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Defines allowed image sources.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: img-src 'self' https://cdn.example.com data:;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;👉 &lt;code&gt;data:&lt;/code&gt; allows base64-encoded images.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-font-src&quot;&gt;📄 &lt;code&gt;font-src&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Controls allowed font sources.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: font-src 'self' https://fonts.gstatic.com;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-connect-src&quot;&gt;🔗 &lt;code&gt;connect-src&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Defines where AJAX/fetch/WebSocket connections can be made.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: connect-src 'self' https://api.example.com;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-media-src&quot;&gt;🎵 &lt;code&gt;media-src&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Specifies allowed sources for &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: media-src 'self' https://media.example.com;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-frame-src-vs-frame-ancestors&quot;&gt;🏗️ &lt;code&gt;frame-src&lt;/code&gt; vs &lt;code&gt;frame-ancestors&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;frame-src&lt;/code&gt;&lt;/strong&gt;: Controls what &lt;em&gt;your app&lt;/em&gt; can embed in an &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: frame-src https://www.youtube.com;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;frame-ancestors&lt;/code&gt;&lt;/strong&gt;: Controls who can embed &lt;em&gt;your site&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: frame-ancestors 'none';
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;✅ Use &lt;code&gt;frame-ancestors&lt;/code&gt; to prevent clickjacking.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-worker-src&quot;&gt;🧩 &lt;code&gt;worker-src&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Defines allowed sources for &lt;strong&gt;Web Workers&lt;/strong&gt; &amp;amp; &lt;strong&gt;Service Workers&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Web Workers&lt;/strong&gt;: Run scripts in a separate thread (background tasks).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service Workers&lt;/strong&gt;: Proxy requests for offline support / caching.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: worker-src 'self' https://cdn.example.com;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-object-src&quot;&gt;📂 &lt;code&gt;object-src&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;Restricts plugins like Flash/Silverlight (legacy). Best to set &lt;code&gt;'none'&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: object-src 'none';
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h4 id=&quot;-reporting-with-csp&quot;&gt;📝 Reporting with CSP&lt;/h4&gt;
&lt;p&gt;Instead of enforcing, you can &lt;strong&gt;test with reporting only&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report-endpoint;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, violations are logged but not blocked. Useful before rollout.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-real-world-example&quot;&gt;💡 Real-World Example&lt;/h3&gt;
&lt;p&gt;Imagine your webapp loads:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scripts from your domain + Google APIs&lt;/li&gt;
&lt;li&gt;Styles from your domain + Google Fonts&lt;/li&gt;
&lt;li&gt;Images from CDN&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CSP could look like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: default-src 'self'; script-src 'self' https://apis.google.com; style-src 'self' https://fonts.googleapis.com; img-src 'self' https://cdn.example.com; connect-src 'self' https://api.myapp.com; object-src 'none'; frame-ancestors 'none'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This protects you from attackers injecting malicious scripts/images from untrusted domains.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-common-csp-issues-developers-face&quot;&gt;🚨 Common CSP Issues Developers Face&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;❌ Images not loading → Forgot &lt;code&gt;img-src&lt;/code&gt; domain.&lt;/li&gt;
&lt;li&gt;❌ Fonts breaking → Missing &lt;code&gt;font-src&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;❌ Videos not playing → &lt;code&gt;media-src&lt;/code&gt; missing.&lt;/li&gt;
&lt;li&gt;❌ API calls failing → Missing &lt;code&gt;connect-src&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;❌ App not embeddable → Strict &lt;code&gt;frame-ancestors&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;👉 If your &lt;strong&gt;frontend/backend look fine but still break&lt;/strong&gt;, check CSP first.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-best-practices-1&quot;&gt;✅ Best Practices&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Always define a strict &lt;code&gt;default-src&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Avoid &lt;code&gt;'unsafe-inline'&lt;/code&gt; and &lt;code&gt;'unsafe-eval'&lt;/code&gt; unless absolutely necessary – use &lt;strong&gt;nonces or hashes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;'self'&lt;/code&gt; wherever possible.&lt;/li&gt;
&lt;li&gt;Test with &lt;code&gt;Content-Security-Policy-Report-Only&lt;/code&gt; before enforcing.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion-3&quot;&gt;📌 Conclusion&lt;/h3&gt;
&lt;p&gt;CSP is not just another header—it’s a &lt;strong&gt;powerful security shield&lt;/strong&gt; 🛡️ for your apps.&lt;br&gt;
The right configuration prevents XSS, clickjacking, and data injection while keeping performance intact.&lt;/p&gt;
&lt;p&gt;👉 The bottom line: &lt;strong&gt;CSP = Control + Security + Peace of Mind&lt;/strong&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23772/content-security-policy-csp-a-complete-guide-for-developers</guid>
<pubDate>Wed, 29 Jul 2026 18:44:08 +0000</pubDate>
</item>
<item>
<title>build, extract, refactor</title>
<link>https://coderlegion.com/23668/build-extract-refactor</link>
<description>&lt;p&gt;Towards the midpoint of last year, I rediscovered one of the developers I found when I started my journey - Mark Tellez. I'll admit, back then I was arrogant and didn't take what he was doing in those old videos seriously, and then he just went off the map.    &lt;/p&gt;
&lt;p&gt;In his livestreams he'd work on either client work or a suggestion from the chat. In everything he made he kept repeating these words: ''build, extract, refactor&quot;  &lt;/p&gt;
&lt;p&gt;He didn't just say them, no. He'd say them and explain to us what they meant in real time...  &lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;build-extract-refactor&quot;&gt;build, extract, refactor&lt;/h3&gt;
&lt;p&gt;As developers we tend to rush to componentise things &lt;em&gt;from&lt;/em&gt; the onset - sometimes when it's not necessary. This is what &lt;strong&gt;build, extract, refactor&lt;/strong&gt; is meant to address &lt;/p&gt;
&lt;h4 id=&quot;build&quot;&gt;build&lt;/h4&gt;
&lt;p&gt;Mark says just build it. Wherever you are in your project, build it. Make it work, don't worry about structure. Your first and most important job is to have it built and working.&lt;/p&gt;
&lt;h4 id=&quot;extract&quot;&gt;extract&lt;/h4&gt;
&lt;p&gt;As you're building, you're going to get to a point where things become too unruly. By this point, you should have noticed a structure forming all on its own - you didn't have to pre-emptively create one.&lt;/p&gt;
&lt;p&gt;It is at this point you then start creating your components using the structure that has &lt;strong&gt;presented itself&lt;/strong&gt;.  &lt;/p&gt;
&lt;h4 id=&quot;refactor&quot;&gt;refactor&lt;/h4&gt;
&lt;p&gt;After you have taken the lead from your project and created your components, then comes the time to refactor them.  &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt; &quot;Don't force it, the structure will reveal itself&quot;&lt;br&gt;
~ Mark Tellez&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-build-extract-refactor-&quot;&gt;Why build, extract, refactor?&lt;/h3&gt;
&lt;p&gt;Now you could be asking yourself; &quot;Why bother doing this? The way I've been making components works just fine&quot;  &lt;/p&gt;
&lt;p&gt;You wouldn't be wrong asking those questions at all.  &lt;/p&gt;
&lt;p&gt;But riddle me this; Have you ever been working on a project and you encounter an error that you can't seem to figure out where exactly it's originating from? Only for you to discover it's from a file you componentised a while ago.  &lt;/p&gt;
&lt;p&gt;You could have avoided this by employing some elements of &lt;strong&gt;build, extract, refactor&lt;/strong&gt; as you will have utilised the intrinsic structure of the project and not a pre-imposed one. Because you have let the project decide, things will have connected logically and tracing of errors is not as difficult.  &lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Now I am not saying this is the be all end all way of building/structuring your projects, no. I am just presenting something  you could consider...  &lt;/p&gt;
&lt;p&gt;Mark wasn't available for the start of this year, he had family responsibilities to handle, I am grateful for all the knowledge he imparted on me the past few months.  &lt;/p&gt;
&lt;p&gt;To this day, I have not come across a developer who will answer your questions in real-time as he's showing why one method is better than the other. One who will take a suggestion and build that suggestion as you watch.  &lt;/p&gt;
&lt;p&gt;Thank you Mark.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;thank-you-for-reading-let-s-connect-&quot;&gt;Thank you for reading, let's connect!&lt;/h4&gt;
&lt;p&gt;Thank you for visiting this little corner of mine. Let's connect on &lt;a rel=&quot;nofollow&quot; href=&quot;https://twitter.com/Psypher1&quot;&gt;Twitter&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.polywork.com/psypher1&quot;&gt;Polywork&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/jamesmidzi/&quot;&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23668/build-extract-refactor</guid>
<pubDate>Tue, 28 Jul 2026 12:05:50 +0000</pubDate>
</item>
<item>
<title>JavaScript Fundamentals</title>
<link>https://coderlegion.com/23355/javascript-fundamentals</link>
<description>&lt;h2 id=&quot;javascript-fundamentals-week-01-understanding-execution-context-scope-amp-closures&quot;&gt;JavaScript Fundamentals (Week-01): Understanding Execution Context, Scope &amp;amp; Closures&lt;/h2&gt;
&lt;blockquote&gt;&lt;p&gt;&quot;When I started learning JavaScript, I could write functions and variables, but I didn't understand what actually happened behind the scenes. Questions like 'Why can an inner function access an outer variable?' or 'Why does JavaScript remember a variable even after a function finishes?' made me curious. This week, I explored three important concepts that answer these questions: &lt;strong&gt;Execution Context&lt;/strong&gt;, &lt;strong&gt;Scope&lt;/strong&gt;, and &lt;strong&gt;Closures&lt;/strong&gt;.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;introduction-1&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;JavaScript is more than just writing variables and functions. Every time we run a JavaScript program, the JavaScript engine creates an environment to execute our code.&lt;/p&gt;
&lt;p&gt;Understanding how JavaScript manages variables, functions, and memory is essential before moving on to advanced topics like asynchronous programming, React, or Node.js.&lt;/p&gt;
&lt;p&gt;In this article, we'll explore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What is Execution Context?&lt;/li&gt;
&lt;li&gt;How JavaScript executes code&lt;/li&gt;
&lt;li&gt;What is Scope?&lt;/li&gt;
&lt;li&gt;Types of Scope&lt;/li&gt;
&lt;li&gt;What is a Closure?&lt;/li&gt;
&lt;li&gt;Why Closures are useful&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-execution-context-&quot;&gt;What is Execution Context?&lt;/h2&gt;
&lt;p&gt;Before JavaScript executes any code, it creates a special environment called the &lt;strong&gt;Execution Context&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Think of it as JavaScript's workspace.&lt;/p&gt;
&lt;p&gt;This workspace contains everything needed to execute the program:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Variables&lt;/li&gt;
&lt;li&gt;Functions&lt;/li&gt;
&lt;li&gt;The value of &lt;code&gt;this&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Scope information&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without an Execution Context, JavaScript wouldn't know where variables or functions exist.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;types-of-execution-context&quot;&gt;Types of Execution Context&lt;/h2&gt;
&lt;p&gt;JavaScript creates two main execution contexts.&lt;/p&gt;
&lt;h3 id=&quot;1-global-execution-context-gec-&quot;&gt;1️⃣ Global Execution Context (GEC)&lt;/h3&gt;
&lt;p&gt;This is created when the program starts.&lt;/p&gt;
&lt;p&gt;There is only &lt;strong&gt;one Global Execution Context&lt;/strong&gt; for each JavaScript program.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;let name = &quot;Sai&quot;;

console.log(name);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before executing this code, JavaScript creates the Global Execution Context.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-function-execution-context-fec-&quot;&gt;2️⃣ Function Execution Context (FEC)&lt;/h3&gt;
&lt;p&gt;Whenever a function is called, JavaScript creates a new Execution Context for that function.&lt;/p&gt;
&lt;p&gt;Example&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function greet() {
    console.log(&quot;Hello&quot;);
}

greet();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When &lt;code&gt;greet()&lt;/code&gt; runs, JavaScript creates a Function Execution Context.&lt;/p&gt;
&lt;p&gt;Once the function finishes, its execution context is removed from memory.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-does-execution-context-work-&quot;&gt;How Does Execution Context Work?&lt;/h2&gt;
&lt;p&gt;Every Execution Context has &lt;strong&gt;two phases&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;phase-1-memory-creation-phase&quot;&gt;Phase 1: Memory Creation Phase&lt;/h3&gt;
&lt;p&gt;Before executing the code, JavaScript scans the program.&lt;/p&gt;
&lt;p&gt;During this phase:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Memory is allocated for variables.&lt;/li&gt;
&lt;li&gt;Function declarations are stored.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;var&lt;/code&gt; variables are initialized with &lt;code&gt;undefined&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; are hoisted but remain in the &lt;strong&gt;Temporal Dead Zone (TDZ)&lt;/strong&gt; until initialized.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;console.log(a);

var a = 10;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Memory created:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;a = undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;phase-2-execution-phase&quot;&gt;Phase 2: Execution Phase&lt;/h3&gt;
&lt;p&gt;Now JavaScript executes the code line by line.&lt;/p&gt;
&lt;p&gt;Variables receive their actual values, and functions are executed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var a = 10;

console.log(a);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;10
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;execution-context-flow&quot;&gt;Execution Context Flow&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Program Starts

        │

        ▼

Global Execution Context

        │

        ▼

Memory Creation Phase

        │

        ▼

Execution Phase

        │

        ▼

Function Call

        │

        ▼

Function Execution Context

        │

        ▼

Memory Phase

        │

        ▼

Execution Phase

        │

        ▼

Function Ends

        │

        ▼

Back to Global Context
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-scope-&quot;&gt;What is Scope?&lt;/h2&gt;
&lt;p&gt;Have you ever wondered why some variables can be accessed everywhere while others cause an error?&lt;/p&gt;
&lt;p&gt;That's because of &lt;strong&gt;Scope&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;definition&quot;&gt;Definition&lt;/h3&gt;
&lt;p&gt;Scope is the area where a variable is accessible.&lt;/p&gt;
&lt;p&gt;Simply put,&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Scope decides where we can use a variable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-is-scope-important-&quot;&gt;Why is Scope Important?&lt;/h2&gt;
&lt;p&gt;Without Scope, every variable would be accessible everywhere.&lt;/p&gt;
&lt;p&gt;This would cause:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Variable conflicts&lt;/li&gt;
&lt;li&gt;Difficult debugging&lt;/li&gt;
&lt;li&gt;Security issues&lt;/li&gt;
&lt;li&gt;Poor code organization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Scope solves these problems by limiting where variables can be accessed.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;types-of-scope&quot;&gt;Types of Scope&lt;/h2&gt;
&lt;h3 id=&quot;global-scope&quot;&gt;Global Scope&lt;/h3&gt;
&lt;p&gt;Variables declared outside all functions belong to the Global Scope.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;let company = &quot;OpenAI&quot;;

function showCompany() {
    console.log(company);
}

showCompany();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;OpenAI
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The variable &lt;code&gt;company&lt;/code&gt; is accessible everywhere.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;function-scope&quot;&gt;Function Scope&lt;/h3&gt;
&lt;p&gt;Variables declared inside a function are available only inside that function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function student() {

    var name = &quot;Sai&quot;;

    console.log(name);

}

student();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Sai
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Trying to access &lt;code&gt;name&lt;/code&gt; outside the function will produce:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;ReferenceError
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;block-scope&quot;&gt;Block Scope&lt;/h3&gt;
&lt;p&gt;Variables declared with &lt;code&gt;let&lt;/code&gt; or &lt;code&gt;const&lt;/code&gt; exist only inside the block &lt;code&gt;{}&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;{
    let city = &quot;Hyderabad&quot;;
    console.log(city);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Hyderabad
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Outside the block:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;console.log(city);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;ReferenceError
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;lexical-scope&quot;&gt;Lexical Scope&lt;/h3&gt;
&lt;p&gt;Lexical Scope means an inner function can access variables declared in its outer function.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function outer() {

    let message = &quot;Hello&quot;;

    function inner() {

        console.log(message);

    }

    inner();

}

outer();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This happens because JavaScript remembers where the function was created.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;scope-chain&quot;&gt;Scope Chain&lt;/h2&gt;
&lt;p&gt;When JavaScript cannot find a variable in the current scope, it searches the parent scope.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Current Scope

↓

Parent Scope

↓

Global Scope

↓

ReferenceError
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This process is called the &lt;strong&gt;Scope Chain&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-a-closure-&quot;&gt;What is a Closure?&lt;/h2&gt;
&lt;p&gt;Closures are one of the most powerful features of JavaScript.&lt;/p&gt;
&lt;h3 id=&quot;definition-1&quot;&gt;Definition&lt;/h3&gt;
&lt;p&gt;A Closure is a function that remembers variables from its outer function even after the outer function has finished executing.&lt;/p&gt;
&lt;p&gt;In simple words,&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;A Closure allows an inner function to continue using variables from its parent function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-are-closures-needed-&quot;&gt;Why are Closures Needed?&lt;/h3&gt;
&lt;p&gt;Normally, local variables disappear after a function finishes.&lt;/p&gt;
&lt;p&gt;Closures allow those variables to stay alive as long as another function still needs them.&lt;/p&gt;
&lt;p&gt;This is useful for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Data privacy&lt;/li&gt;
&lt;li&gt;Counters&lt;/li&gt;
&lt;li&gt;Event handlers&lt;/li&gt;
&lt;li&gt;Module patterns&lt;/li&gt;
&lt;li&gt;Maintaining state&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;closure-example&quot;&gt;Closure Example&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;function counter() {

    let count = 0;

    return function () {

        count++;

        console.log(count);

    };

}

const increment = counter();

increment();
increment();
increment();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;1
2
3
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-does-this-work-&quot;&gt;How Does This Work?&lt;/h2&gt;
&lt;p&gt;When &lt;code&gt;counter()&lt;/code&gt; is called:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JavaScript creates &lt;code&gt;count&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It returns the inner function.&lt;/li&gt;
&lt;li&gt;Even after &lt;code&gt;counter()&lt;/code&gt; finishes, the inner function still remembers &lt;code&gt;count&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Every time &lt;code&gt;increment()&lt;/code&gt; is called:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;count&lt;/code&gt; increases by one.&lt;/li&gt;
&lt;li&gt;The value is preserved because of the Closure.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;key-takeaways-1&quot;&gt;Key Takeaways&lt;/h2&gt;
&lt;p&gt;✔ JavaScript creates an Execution Context before executing code.&lt;/p&gt;
&lt;p&gt;✔ Every Execution Context has a Memory Creation Phase and an Execution Phase.&lt;/p&gt;
&lt;p&gt;✔ Scope determines where variables can be accessed.&lt;/p&gt;
&lt;p&gt;✔ JavaScript supports Global Scope, Function Scope, Block Scope, and Lexical Scope.&lt;/p&gt;
&lt;p&gt;✔ Closures allow functions to remember variables from their outer scope.&lt;/p&gt;
&lt;p&gt;✔ Closures are commonly used for data privacy, counters, and maintaining state.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;conclusion-2&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Understanding &lt;strong&gt;Execution Context&lt;/strong&gt;, &lt;strong&gt;Scope&lt;/strong&gt;, and &lt;strong&gt;Closures&lt;/strong&gt; changed the way I think about JavaScript. These concepts explain what happens behind the scenes whenever our code runs. Once you understand them, advanced topics like asynchronous programming, callbacks, Promises, React Hooks, and Node.js become much easier to learn.&lt;/p&gt;
&lt;p&gt;I hope this article helped you build a stronger JavaScript foundation. If you're learning JavaScript too, feel free to share your thoughts or ask questions in the comments.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23355/javascript-fundamentals</guid>
<pubDate>Thu, 23 Jul 2026 17:20:41 +0000</pubDate>
</item>
<item>
<title>C++ Traps That Caught Me Off Guard — A Beginner's Log</title>
<link>https://coderlegion.com/23326/c-traps-that-caught-me-off-guard-a-beginners-log</link>
<description>&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;Learning C++ has been a journey full of traps. Here are three I hit — hope they save you some debugging time.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;-trap-1-new-delete-instead-of-delete-&quot;&gt;🎣 Trap 1: &lt;code&gt;new[]&lt;/code&gt; + &lt;code&gt;delete&lt;/code&gt;(instead of &lt;code&gt;delete[]&lt;/code&gt;)&lt;/h3&gt;
&lt;h4 id=&quot;wrong-code&quot;&gt;Wrong Code&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt; int* a = new int[3];
 
 // delete a; ❌ --&amp;gt; undefined
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;class MyClass{
public:
~MyClass(){
std::cout&amp;lt;&amp;lt;&quot;delete successfully&quot;&amp;lt;&amp;lt;std::endl;
}
}
int main(){
MyClass* mc = new MyClass[2];
//delete mc;  ❌ -&amp;gt; only outputs one &quot;delete successfully&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;what-happens&quot;&gt;What Happens&lt;/h4&gt;
&lt;p&gt;C++ standard clearly states that this is &lt;strong&gt;undefined behavior&lt;/strong&gt;. Relying on it is extremely dangerous — different compilers, optimization levels, and platforms may behave completely differently.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;new[]&lt;/code&gt; + &lt;code&gt;delete&lt;/code&gt; only calls the destructor once, while &lt;code&gt;delete[]&lt;/code&gt; calls it for every element in the array. Using &lt;code&gt;delete&lt;/code&gt; instead of &lt;code&gt;delete[]&lt;/code&gt; causes resource leaks for the remaining elements. &lt;/p&gt;
&lt;h4 id=&quot;how-to-fix&quot;&gt;How to Fix&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt; int* a = new int[3];
 
 delete[] a;  -&amp;gt; ✔
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;class MyClass{
public:
~MyClass(){
std::cout&amp;lt;&amp;lt;&quot;delete sucessfully&quot;&amp;lt;&amp;lt;std::endl;
}
};
int main(){
MyClass* mc = new MyClass[2];
delete[] mc; -&amp;gt; ✔ outputs &quot;delete successfully&quot; twice
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-trap-2-cin-getline-eating-your-input&quot;&gt;🎣 Trap 2: &lt;code&gt;cin&lt;/code&gt; + &lt;code&gt;getline&lt;/code&gt; eating your input&lt;/h3&gt;
&lt;h4 id=&quot;wrong-code-1&quot;&gt;Wrong Code&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;int age;
std::string name;

std::cout&amp;lt;&amp;lt;&quot;Enter age: &quot;;
std::cin&amp;gt;&amp;gt;age;

std::cout&amp;lt;&amp;lt;&quot;Enter name: &quot;;
std::getline(cin, name); // ❌ name ends up empty!

std::cout&amp;lt;&amp;lt;name&amp;lt;&amp;lt;std::endl;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;what-happens-1&quot;&gt;What Happens&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;cin&lt;/code&gt; leaves a newline character (&lt;code&gt;\n&lt;/code&gt;) in the input buffer, and &lt;code&gt;getline&lt;/code&gt; reads it immediately — so it stops before you even type anything.&lt;/p&gt;
&lt;h4 id=&quot;why&quot;&gt;Why&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;cin &amp;gt;&amp;gt;&lt;/code&gt; reads the value you type but leaves &lt;code&gt;\n&lt;/code&gt; in the buffer. &lt;code&gt;getline&lt;/code&gt; stops reading when it encounters &lt;code&gt;\n&lt;/code&gt;, so it grabs that leftover newline and returns an empty string.&lt;/p&gt;
&lt;h4 id=&quot;how-to-fix-1&quot;&gt;How to Fix&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;std::cin &amp;gt;&amp;gt; age;
cin.ignore();    // 👈 Eat the leftover newline
std::getline(cin, name);
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-trap-3-copy-constructor-vs-copy-assignment&quot;&gt;🎣 Trap 3: &lt;code&gt;Copy constructor&lt;/code&gt; vs &lt;code&gt;copy assignment&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;class FixArray{
    std::string name_;
    int* data_;
    size_t size_;
public:
~FixArray(){
        delete[] data_;
    }

    FixArray(const FixArray&amp;amp; other) : FixArray(other.size_, other.name_){
        std::copy(other.data_, other.data_ + size_, data_);
    }

    FixArray&amp;amp; operator=(const FixArray&amp;amp; other){
        if(this != &amp;amp;other){
            int* new_data = new int[other.size_];
            std::copy(other.data_, other.data_ + other.size_, new_data);
            delete[] data_;
            data_ = new_data;
            size_ = other.size_;
            name_ = other.name_;
        }
        return *this;
    } 
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Many beginners can't tell the difference between a &lt;strong&gt;copy constructor&lt;/strong&gt; and a &lt;strong&gt;copy assignment&lt;/strong&gt;. Actually, it's quite simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;// Copy constructor: creating a new object from an existing one
FixArray fa(10, &quot;example&quot;);
FixArray fb = fa;    // 👈 copy constructor called

// Copy assignment: assigning to an already-existing object
FixArray fc(5, &quot;other&quot;);
fc = fa;            // 👈 copy assignment called
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Copy constructor&lt;/strong&gt; — creates a brand new object as a copy of an existing one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Copy assignment&lt;/strong&gt; — replaces the contents of an already-existing object with another object's data.&lt;/p&gt;
&lt;p&gt;The key difference: &lt;strong&gt;copy constructor builds something from nothing; copy assignment replaces something that already exists.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&amp;gt;&lt;em&gt;These are three traps I hit while learning C++. If you're a beginner too, I hope this saves you some debugging time. Have you encountered other tricky C++ traps? Share them below!&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23326/c-traps-that-caught-me-off-guard-a-beginners-log</guid>
<pubDate>Thu, 23 Jul 2026 09:28:37 +0000</pubDate>
</item>
<item>
<title>📦 The Ultimate Guide to package.json, package-lock.json, .yarnrc, babel.config,tsconfig.json &amp; More</title>
<link>https://coderlegion.com/23270/the-ultimate-guide-package-json-package-lock-json-yarnrc-babel-config-tsconfig-json-more</link>
<description>&lt;p&gt;Most developers use these config files daily — but &lt;strong&gt;few truly understand how they work&lt;/strong&gt;, why they matter, and how small mistakes can break a project or waste hours of debugging time.&lt;/p&gt;
&lt;p&gt;This post goes beyond definitions: we’ll explore &lt;strong&gt;how these files work, why they matter, best practices, and common real-world mistakes to avoid&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;1-package-json&quot;&gt;1️⃣ &lt;code&gt;package.json&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What it is:&lt;/strong&gt;&lt;br&gt;
The heart of any Node.js or React project.&lt;br&gt;
It lists the project’s metadata, dependencies, scripts and configuration.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;
Acts as the &lt;strong&gt;project manifest&lt;/strong&gt; — when you run &lt;code&gt;npm install&lt;/code&gt;, it reads the dependencies and installs them.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Important things to remember:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep dependencies (&lt;code&gt;dependencies&lt;/code&gt;) and dev tools (&lt;code&gt;devDependencies&lt;/code&gt;) separate.&lt;/li&gt;
&lt;li&gt;Always update versions carefully — check changelogs when upgrading.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;👉 Quick Difference:&lt;/strong&gt;&lt;code&gt;dependencies&lt;/code&gt; are needed to &lt;strong&gt;run the application in production&lt;/strong&gt;, while &lt;code&gt;devDependencies&lt;/code&gt; are only needed for &lt;strong&gt;development, testing and build processes&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define clear &lt;code&gt;scripts&lt;/code&gt; (e.g., &lt;code&gt;npm run test&lt;/code&gt;, &lt;code&gt;npm run lint&lt;/code&gt;) to standardize tasks for all developers.&lt;/li&gt;
&lt;li&gt;Use semantic versioning (&lt;code&gt;^&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;, exact versions) wisely. The versioning symbols define the range of versions allowed, and npm/yarn automatically picks the newest allowed version (unless restricted by the lockfile).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-world tip:&lt;/strong&gt;&lt;br&gt;
Messy or outdated &lt;code&gt;package.json&lt;/code&gt; often causes &lt;strong&gt;“it works on my machine”&lt;/strong&gt; issues, especially in CI/CD. Always commit updated files!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;2-package-lock-json-yarn-lock&quot;&gt;2️⃣ &lt;code&gt;package-lock.json&lt;/code&gt; / &lt;code&gt;yarn.lock&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How they work:&lt;/strong&gt;&lt;br&gt;
Record &lt;strong&gt;exact package versions&lt;/strong&gt; (including nested dependencies) used at install time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Why they matter in CI/CD:&lt;/strong&gt;&lt;br&gt;
Without lock files, your CI pipeline might install newer versions of packages than local dev, causing unpredictable failures.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Always commit lock files to version control.&lt;/li&gt;
&lt;li&gt;Never manually edit lock files.&lt;/li&gt;
&lt;li&gt;Avoid deleting lock files just to “fix” install issues — this usually hides the real problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-world mistake:&lt;/strong&gt;&lt;br&gt;
A team skips committing &lt;code&gt;package-lock.json&lt;/code&gt;; their staging environment works, but production deploy breaks because a sub-dependency released a breaking change overnight.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;🔑 Important to know:&lt;/strong&gt;&lt;br&gt;
The &lt;strong&gt;lockfile (package-lock.json or yarn.lock)&lt;/strong&gt; pins the exact version after the first install. Even if your package.json allows a higher version, the lockfile keeps things stable until you explicitly update.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;3-yarnrc-npmrc&quot;&gt;3️⃣ &lt;code&gt;.yarnrc&lt;/code&gt; / &lt;code&gt;.npmrc&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How they work:&lt;/strong&gt;&lt;br&gt;
Customize the behavior of npm/yarn, e.g., set private registries or proxies.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Important to remember:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep sensitive tokens out of versioned &lt;code&gt;.npmrc&lt;/code&gt; / &lt;code&gt;.yarnrc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For private registries, use &lt;code&gt;.npmrc&lt;/code&gt; in the CI/CD pipeline or environment variables.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Store per-user configs in your home directory (&lt;code&gt;~/.npmrc&lt;/code&gt;), not in the project if they contain sensitive info.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;4-babel-config-js-babelrc&quot;&gt;4️⃣ &lt;code&gt;babel.config.js&lt;/code&gt; / &lt;code&gt;.babelrc&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;
Transforms modern JavaScript (ES6+, JSX, TypeScript) into code browsers understand.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Common mistakes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Missing or misconfigured presets cause build failures.&lt;/li&gt;
&lt;li&gt;Using incompatible Babel and Webpack versions leads to hours of confusing errors.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Explicitly define presets (&lt;code&gt;@babel/preset-env&lt;/code&gt;, &lt;code&gt;@babel/preset-react&lt;/code&gt;) in projects.&lt;/li&gt;
&lt;li&gt;Keep Babel versions in sync with other tools.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;5-tsconfig-json&quot;&gt;5️⃣ &lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;
Controls TypeScript’s compiler behavior and type checking.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Critical settings:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;strict&lt;/code&gt; → enables full type safety.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;target&lt;/code&gt; → defines output JS version.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;baseUrl&lt;/code&gt; / &lt;code&gt;paths&lt;/code&gt; → improve import paths and avoid ugly &lt;code&gt;../../&lt;/code&gt; imports.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use strict mode in production projects.&lt;/li&gt;
&lt;li&gt;Align &lt;code&gt;tsconfig.json&lt;/code&gt; with CI/CD type-check steps.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-world issue:&lt;/strong&gt;&lt;br&gt;
You skip strict mode to save time, ship to production, and later discover subtle type bugs that could have been caught early.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;6-eslint-json-eslintrc-js&quot;&gt;6️⃣ &lt;code&gt;.eslint.json&lt;/code&gt; / &lt;code&gt;.eslintrc.js&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;
Defines code linting rules — runs in editors, CI pipelines, or as pre-commit hooks.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use a shared team config or extend popular configs like Airbnb or StandardJS.&lt;/li&gt;
&lt;li&gt;Integrate linting into CI to prevent code quality regressions.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real-world example:&lt;/strong&gt;&lt;br&gt;
Teams without enforced linting often waste time reviewing minor formatting issues in PRs.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;7-prettierrc&quot;&gt;7️⃣ &lt;code&gt;.prettierrc&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;
Configures automatic code formatting.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run Prettier in your editor + CI pipeline.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;.prettierignore&lt;/code&gt; to exclude large generated files.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;8-gitignore&quot;&gt;8️⃣ &lt;code&gt;.gitignore&lt;/code&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;
Tells Git which files/folders to skip tracking (like &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Best practices:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep your &lt;code&gt;.gitignore&lt;/code&gt; up to date; accidentally committing &lt;code&gt;node_modules&lt;/code&gt; can break builds or bloat the repo.&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;.gitignore&lt;/code&gt; on new clones to avoid pushing unwanted files.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-why-it-all-matters-in-ci-cd&quot;&gt;💥 Why It All Matters in CI/CD&lt;/h3&gt;
&lt;p&gt;CI/CD systems rely on &lt;strong&gt;predictable, repeatable builds&lt;/strong&gt;. These config files:&lt;/p&gt;
&lt;p&gt;✅ Ensure consistent dependency versions&lt;br&gt;
✅ Standardize linting and formatting&lt;br&gt;
✅ Prevent committing sensitive or unwanted files&lt;br&gt;
✅ Configure tools like Babel, TypeScript, Webpack correctly across environments&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Without understanding them, you risk:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Broken deploys&lt;/li&gt;
&lt;li&gt;Hard-to-debug environment differences&lt;/li&gt;
&lt;li&gt;Hours of wasted developer time chasing “mystery” bugs&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;💥 Common Mistakes to Avoid&lt;/strong&gt;&lt;br&gt;
Deleting lockfiles to “fix” install errors → leads to unpredictable builds.&lt;/p&gt;
&lt;p&gt;Ignoring Babel or TypeScript warnings → causes runtime crashes.&lt;/p&gt;
&lt;p&gt;Not updating scripts in package.json → CI pipelines skip critical checks.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-final-thoughts-3&quot;&gt;💡 Final Thoughts&lt;/h3&gt;
&lt;p&gt;Understanding these config files isn’t just “setup work”—it’s the foundation of reliable, maintainable apps. Whether you’re a frontend dev or working on CI/CD, mastering these files will save hours of debugging and make you a stronger engineer.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23270/the-ultimate-guide-package-json-package-lock-json-yarnrc-babel-config-tsconfig-json-more</guid>
<pubDate>Wed, 22 Jul 2026 16:56:11 +0000</pubDate>
</item>
<item>
<title>Coming Back to Writing - and My First Cybersecurity Project</title>
<link>https://coderlegion.com/23014/coming-back-to-writing-and-my-first-cybersecurity-project</link>
<description>&lt;p&gt;It's been a while since I've written anything publicly. I used to be fairly active documenting what I was building as a developer, and somewhere along the way - between shipping products and then pivoting toward cybersecurity - I went quiet. This is me picking that habit back up, starting with the most honest thing I can write about: a small, beginner-level tool I just finished.&lt;/p&gt;
&lt;p&gt;I'm not writing this to announce a launch. I'm writing it because documenting the learning is the actual point.&lt;/p&gt;
&lt;h3 id=&quot;where-i-m-at&quot;&gt;Where I'm At&lt;/h3&gt;
&lt;p&gt;I come from a full-stack development background - I've shipped a few SaaS products solo. Over the past several months I've been transitioning into cybersecurity, specifically working toward a SOC Analyst role. I'm doing hands-on labs on LetsDefend, going through ISC2 CC and Google Cybersecurity material, and generally trying to learn by doing rather than just collecting certificates.&lt;/p&gt;
&lt;p&gt;One thing I noticed early: every SOC case follows a similar manual pattern. You get an IOC - an IP, a domain, a file hash - and you check it across two or three different threat intel sources by hand. VirusTotal here, AbuseIPDB there, MalwareBazaar if it's a hash. Same value, three tabs, three logins, three waits.&lt;/p&gt;
&lt;p&gt;So as a first project bridging my dev background with what I'm learning in security, I built a small tool that does that lookup automatically.&lt;/p&gt;
&lt;h3 id=&quot;what-i-built-and-what-it-isn-t-&quot;&gt;What I Built (and What It Isn't)&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;IOC Enricher&lt;/strong&gt; - paste an IP, domain, or hash, it queries VirusTotal, AbuseIPDB, and MalwareBazaar in parallel, and gives back a combined risk score with a plain-English AI summary.&lt;/p&gt;
&lt;p&gt;I want to be upfront: this is not a novel idea. Tools like this already exist - some far more polished, built by people with actual SOC experience. I'm not claiming to have invented anything. What this project actually is: a way to force myself to touch real APIs, handle real failure cases, and understand the manual workflow well enough to automate a small piece of it.&lt;/p&gt;
&lt;p&gt;It's a beginner project. I built it knowing that.&lt;/p&gt;
&lt;p&gt;Live: &lt;a rel=&quot;nofollow&quot; href=&quot;https://ioc-enricher-abd.vercel.app/&quot;&gt;https://ioc-enricher-abd.vercel.app/&lt;/a&gt; &lt;br&gt;
Code: github.com/m-abdullah-06/ioc-enricher&lt;/p&gt;
&lt;h3 id=&quot;what-i-learned-building-it&quot;&gt;What I Learned Building It&lt;/h3&gt;
&lt;p&gt;The stack itself was straightforward - FastAPI backend, Next.js frontend, Groq for the AI summary. Nothing exotic. The useful part was what went wrong.&lt;/p&gt;
&lt;p&gt;MalwareBazaar, for instance, silently returned &quot;not found&quot; for every hash I tested - including ones I knew were malicious. My first assumption was that the hashes just weren't in their database. Turns out abuse.ch now requires an Auth-Key header on every request, and my code was getting a 401 that my error handling quietly swallowed. The tool wasn't broken - it just looked like it was working while giving me nothing.&lt;/p&gt;
&lt;p&gt;That was the real lesson. Not the fix itself, which took five minutes once I found it, but the realization that a silent failure that looks like a valid result is worse than an obvious crash. If this were a real SOC tool and not a portfolio piece, that bug would mean telling someone a file was clean when it was never actually checked.&lt;/p&gt;
&lt;h3 id=&quot;why-i-m-writing-this-instead-of-staying-quiet&quot;&gt;Why I'm Writing This Instead of Staying Quiet&lt;/h3&gt;
&lt;p&gt;I could've kept this to myself until I had something more impressive to show. But I think there's more value in documenting the beginner version honestly - partly for my own record of where I started, and partly because I know there are other people making the same dev-to-security switch who might find the process more useful than a polished result.&lt;/p&gt;
&lt;p&gt;I'll keep building on this - bulk lookups, maybe Shodan integration, better error handling than the silent-failure lesson taught me I needed. But this version, the rough first one, is the one worth writing about.&lt;/p&gt;
&lt;p&gt;More writeups coming as I close more SOC cases and keep building. Good to be back.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Muhammad Abdullah - learning SOC analysis, building things along the way. GitHub: m-abdullah-06&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23014/coming-back-to-writing-and-my-first-cybersecurity-project</guid>
<pubDate>Sun, 19 Jul 2026 11:23:26 +0000</pubDate>
</item>
<item>
<title>Understanding Backpropagation with micrograd — Principles, Intuition, and Pitfalls I Ran Into</title>
<link>https://coderlegion.com/22806/understanding-backpropagation-with-micrograd-principles-intuition-and-pitfalls-into</link>
<description>&lt;blockquote&gt;&lt;p&gt;I had heard about &lt;strong&gt;backpropagation&lt;/strong&gt; many times, but I never really understood how it actually worked. When someone asked me what backpropagation was, all I could say was, &quot;It's how neural networks learn… I guess?&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;I'd seen the formulas, I knew the chain rule, but it always felt like there was a wall between me and true understanding.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;So I spent two days reading &lt;strong&gt;Karpathy&lt;/strong&gt;'s &lt;strong&gt;micrograd&lt;/strong&gt; project source code. It's honestly the best explanation of the core principles of neural networks I've ever come across!&lt;br&gt;
👉 &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/karpathy/micrograd&quot;&gt;https://github.com/karpathy/micrograd&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This post documents what I learned and how I understand it now.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-post&quot;&gt;Why This Post&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I'd watched plenty of YouTube videos explaining backpropagation, but most of them just said:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Backpropagation is repeatedly applying the chain rule.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That's true, but for a beginner, it's basically saying nothing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I was used to frameworks where a single line — &lt;code&gt;loss.backward()&lt;/code&gt; — got the job done, with no idea what was actually happening under the hood.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-key-code-amp-what-it-does&quot;&gt;The Key Code &amp;amp; What It Does&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;class Value:
    def __init__(self, data, _child = (), _op = &quot;&quot;, label = &quot;&quot;):
        self.data = data
        self._prev = set(_child)
        self._backward = lambda: None
        self.grad = 0.0
        self._op = _op
        self.label = label
    
    def __repr__(self):
        return f&quot;Value(data = {self.data})&quot;

    def __add__(self, other):
        other = other if isinstance(other, Value) else Value(other)
        out = Value(self.data + other.data, (self, other), &quot;+&quot;)

       def _backward():
            self.grad += out.grad * 1.0
            other.grad += out.grad * 1.0
        out._backward = _backward
        return out

     def backward(self):

        topo = []
        visited = set()
        def build_topo(v):
            if v not in visited:
                visited.add(v)
                for child in v._prev:
                    build_topo(child)
                topo.append(v)
        self.grad = 1.0
        build_topo(self)

        for node in reversed(topo):
            node._backward()

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;micrograd also defines &lt;code&gt;__mul__&lt;/code&gt;, &lt;code&gt;tanh&lt;/code&gt;, &lt;code&gt;__pow__&lt;/code&gt; and other methods — they all work the same way, each attaching a different &lt;code&gt;_backward&lt;/code&gt; note. So I'm only showing the core code here.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;&lt;p&gt;Try this:&lt;br&gt;
a = Value(1.0)&lt;br&gt;
b = Value(2.0)&lt;br&gt;
n = a + b&lt;br&gt;
L = n.tanh()&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You'll get this result:&lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=9064488572173869505&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h3&gt;
&lt;p&gt;When you write &lt;code&gt;n = a + b&lt;/code&gt;, here's what happens:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A new &lt;code&gt;Value&lt;/code&gt; instance &lt;code&gt;n&lt;/code&gt; is created.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;_backward&lt;/code&gt; function is attached to &lt;code&gt;n._backward()&lt;/code&gt; but &lt;strong&gt;not executed yet&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;backward()&lt;/code&gt; is called, it traverses the graph in reverse topological order, executing each node's &lt;code&gt;_backward()&lt;/code&gt; one by one.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;things-you-should-know&quot;&gt;Things You Should Know&lt;/h3&gt;
&lt;blockquote&gt;&lt;p&gt;Chain Rule:&lt;br&gt;
&lt;code&gt;∂e/∂a = (∂e/∂c)(∂c/∂a) + (∂e/∂d)(∂d/∂a)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;At first I didn't accumulate gradients in &lt;code&gt;_backward()&lt;/code&gt; — just assigned them. That led to some serious bugs.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Imagine you write:&lt;br&gt;
a = Value(2.0)&lt;br&gt;
b = a + a&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What do you expect?&lt;br&gt;
&lt;strong&gt;a.grad = 2, b.grad = 1?&lt;/strong&gt; Or &lt;strong&gt;a.grad = 1, b.grad = 1?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you run it, you'll find &lt;code&gt;a.grad = 1&lt;/code&gt; — which is completely wrong!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;    def __add__(self, other):
    other = other if isinstance(other, Value) else Value(other)
    out = Value(self.data + other.data, (self, other), &quot;+&quot;)
    def _backward():
        self.grad = out.grad * 1.0   # self = a, other = a;
        other.grad = out.grad * 1.0  # both set a.grad = 1
    out._backward = _backward
    return out
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;p&gt;Here's another example:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Try this:&lt;br&gt;
a = Value(2.0)&lt;br&gt;
b = Value(3.0)&lt;br&gt;
c = a + b&lt;br&gt;
d = a &lt;em&gt; b&lt;br&gt;
e = c &lt;/em&gt; d&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What are &lt;code&gt;a.grad&lt;/code&gt; and &lt;code&gt;b.grad&lt;/code&gt; this time?&lt;br&gt;
&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=12734637975006860424&quot; alt=&quot;&quot;&gt;&lt;br&gt;
&lt;strong&gt;Clearly, the gradients of &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; got overwritten!&lt;/strong&gt;&lt;br&gt;
So &lt;strong&gt;you MUST accumulate in &lt;code&gt;_backward()&lt;/code&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;my-confusion&quot;&gt;My Confusion&lt;/h3&gt;
&lt;blockquote&gt;&lt;p&gt;For a long time I couldn't understand what &lt;code&gt;def _backward()&lt;/code&gt; and &lt;code&gt;out._backward = _backward&lt;/code&gt; were really doing. Sounds silly, I know.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I kept wondering: when &lt;code&gt;Value c&lt;/code&gt; is created, &lt;code&gt;out.grad&lt;/code&gt; defaults to 0 — how can it possibly backpropagate to find &lt;code&gt;self.grad&lt;/code&gt; and &lt;code&gt;other.grad&lt;/code&gt;? (Of course, back then I thought &lt;code&gt;out._backward = _backward&lt;/code&gt; would immediately execute the &lt;code&gt;_backward&lt;/code&gt; function 😅)&lt;/p&gt;
&lt;p&gt;Now I finally get it:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;After creating a new &lt;code&gt;Value c&lt;/code&gt;, &lt;code&gt;c._backward&lt;/code&gt; just stores the &lt;code&gt;_backward&lt;/code&gt; function without running it. It only executes when &lt;code&gt;backward()&lt;/code&gt; is called, traversing in reverse topological order.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;It's a naive question that came from a naive assumption, but I hope it helps other developers who've been stuck on the same thing.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-thought-that-followed&quot;&gt;A Thought That Followed&lt;/h3&gt;
&lt;blockquote&gt;&lt;p&gt;Imagine this:&lt;br&gt;
a = Value(2.0)&lt;br&gt;
b = Value(3.0)&lt;br&gt;
c = Value(2.0)&lt;br&gt;
d = a + b&lt;br&gt;
e = c + d&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Do &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, and &lt;code&gt;c&lt;/code&gt; have their &lt;code&gt;_backward&lt;/code&gt; executed?&lt;/p&gt;
&lt;p&gt;Answer: All three have &lt;code&gt;_backward&lt;/code&gt; set to &lt;code&gt;lambda: None&lt;/code&gt; (an empty function). When backpropagation reaches them, nothing happens — their gradients have already been accumulated through the upstream path.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-i-gained&quot;&gt;What I Gained&lt;/h3&gt;
&lt;p&gt;After carefully reading through &lt;strong&gt;Karpathy&lt;/strong&gt;'s &lt;strong&gt;micrograd&lt;/strong&gt; source code, I was genuinely impressed. I'd never understood the core of &lt;strong&gt;backpropagation&lt;/strong&gt; so intuitively before. Here's what I took away:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I now understand the core mechanism of &lt;strong&gt;backpropagation&lt;/strong&gt; — &lt;code&gt;.backward()&lt;/code&gt; is no longer a black box to me.&lt;/li&gt;
&lt;li&gt;I finally &lt;strong&gt;truly understand&lt;/strong&gt; what &quot;backpropagation is just repeatedly applying the chain rule&quot; means.&lt;/li&gt;
&lt;li&gt;This has kicked off my deep dive into &lt;strong&gt;deep learning&lt;/strong&gt;, and now I can better compare &lt;strong&gt;micrograd&lt;/strong&gt; with &lt;strong&gt;PyTorch&lt;/strong&gt; and modern neural network frameworks.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;closing-thoughts&quot;&gt;Closing Thoughts&lt;/h3&gt;
&lt;p&gt;Thanks for taking the time to read this. I wrote it right after going through the source code, so there may be mistakes in my understanding — corrections are very welcome.&lt;/p&gt;
&lt;p&gt;If you're also a beginner in &lt;strong&gt;deep learning&lt;/strong&gt;, feel free to reach out. I'd love to learn together with you.&lt;/p&gt;
&lt;p&gt;If you enjoyed this post, don't forget to like, bookmark, comment, and follow! I'll be sharing my learning journey in &lt;strong&gt;deep learning&lt;/strong&gt;, &lt;strong&gt;machine learning&lt;/strong&gt;, &lt;strong&gt;backend development&lt;/strong&gt;, and more from time to time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check out my Dev.to&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/elysianx138&quot;&gt;https://dev.to/elysianx138&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;My GitHub will also track my learning progress&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/elysianx138&quot;&gt;https://github.com/elysianx138&lt;/a&gt;  &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22806/understanding-backpropagation-with-micrograd-principles-intuition-and-pitfalls-into</guid>
<pubDate>Thu, 16 Jul 2026 11:48:52 +0000</pubDate>
</item>
<item>
<title>From &quot;I Understood Nothing&quot; to Building a RAG App</title>
<link>https://coderlegion.com/22742/from-i-understood-nothing-to-building-a-rag-app</link>
<description>&lt;p&gt;Yesterday, I took the time to go through 31 pages of my own notes on NLP, notes I had carefully created during an AI engineering course. However, I found that I understood very little of it.&lt;/p&gt;
&lt;p&gt;It felt odd and disheartening to have &lt;em&gt;written something down&lt;/em&gt; yet not grasp it. I initially thought the issue was with me. But that wasn't the case. The real issue was that simply reading notes does not equate to true learning. My notes were packed with information intended for a version of me that already had some understanding, and you can't develop comprehension just by rereading a summary. So, I decided to change my approach. Instead of reading, I had the concepts explained to me one question at a time, starting from the basics, using only simple examples. No technical terms were permitted until I had proven I could understand them.&lt;/p&gt;
&lt;p&gt;By the end of the day, I had reconstructed the four main concepts of practical NLP from the ground up, and I used them to create a functioning app that answers questions based on my own notes. Here's the complete journey, with the same examples that finally helped me understand, along with the code for each step so you can follow along.&lt;/p&gt;
&lt;h3 id=&quot;start-with-the-dumbest-possible-question&quot;&gt;Start with the dumbest possible question&lt;/h3&gt;
&lt;p&gt;Imagine you want a computer to sort emails into &quot;spam&quot; and &quot;not spam.&quot; Here's the catch: the computer has never read a word in its life. The only thing it can do, the only thing it has &lt;em&gt;ever&lt;/em&gt; been able to do, is math on numbers.&lt;/p&gt;
&lt;p&gt;So before it can sort a single email, one problem comes first: &lt;strong&gt;the words have to become numbers.&lt;/strong&gt; That sentence turned out to be the foundation of everything. All of this branch of NLP is just one job: turn text into numbers without losing the meaning that matters.&lt;/p&gt;
&lt;p&gt;My first instinct was to turn each &lt;em&gt;letter&lt;/em&gt; into a number. It's a reasonable guess, and it's actually how computers store text underneath. But it captures &lt;em&gt;spelling&lt;/em&gt;, not &lt;em&gt;meaning&lt;/em&gt;. &quot;win&quot; and &quot;bin&quot; look almost identical as numbers, yet mean totally different things. The meaning of an email lives in its &lt;strong&gt;words&lt;/strong&gt;, not its letters.&lt;/p&gt;
&lt;h3 id=&quot;bag-of-words-count-don-t-spell&quot;&gt;Bag of Words: count, don't spell&lt;/h3&gt;
&lt;p&gt;So we work with whole words. Take three emails:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;win money now&quot;&lt;/li&gt;
&lt;li&gt;&quot;money money money&quot;&lt;/li&gt;
&lt;li&gt;&quot;see you tomorrow&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;List every distinct word (that's the vocabulary), give each a column, and for each email count how many times each word appears. &quot;win money now&quot; becomes &lt;code&gt;1, 1, 1, 0, 0, 0&lt;/code&gt;. &quot;money money money&quot; becomes &lt;code&gt;0, 3, 0, 0, 0, 0&lt;/code&gt;. Suddenly every email is a row of pure numbers, and a computer can compare them.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;scikit-learn&lt;/code&gt; does exactly this in three lines:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from sklearn.feature_extraction.text import CountVectorizer

emails = [&quot;win money now&quot;, &quot;money money money&quot;, &quot;see you tomorrow&quot;]

vectorizer = CountVectorizer()
X = vectorizer.fit_transform(emails)

print(vectorizer.get_feature_names_out())  # the vocabulary (the columns)
print(X.toarray())                         # each row = one email as word-counts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's it. That's the intimidating-sounding &quot;Bag of Words.&quot; I had read about it and bounced off it; here I &lt;em&gt;did&lt;/em&gt; it by hand in two minutes, then in three lines of code.&lt;/p&gt;
&lt;p&gt;It's called a &lt;em&gt;bag&lt;/em&gt; for a reason, and the reason is also its flaw. Imagine tossing a sentence's words into a paper bag and shaking it. You still know &lt;em&gt;which&lt;/em&gt; words are inside and &lt;em&gt;how many&lt;/em&gt;, but the order is gone. &quot;win money now&quot; and &quot;now win money&quot; produce the identical row. Usually harmless. But consider &lt;strong&gt;&quot;dog bites man&quot;&lt;/strong&gt; versus &lt;strong&gt;&quot;man bites dog&quot;&lt;/strong&gt;: same three words, one is a boring Tuesday and the other is front-page news. To Bag of Words they're identical. That weakness is exactly why fancier methods had to be invented later.&lt;/p&gt;
&lt;h3 id=&quot;tf-idf-not-all-words-deserve-equal-weight&quot;&gt;TF-IDF: not all words deserve equal weight&lt;/h3&gt;
&lt;p&gt;Bag of Words has a second problem: it treats every word as equally important. The word &quot;the&quot; is in nearly every email, so knowing an email contains &quot;the&quot; tells you &lt;em&gt;nothing&lt;/em&gt;. Meanwhile a rare word like &quot;viagra&quot; is a screaming signal. Yet Bag of Words counts them the same.&lt;/p&gt;
&lt;p&gt;TF-IDF fixes this with two dials multiplied together:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TF (term frequency)&lt;/strong&gt;: how often a word appears &lt;em&gt;in this one email&lt;/em&gt;. If a message says &quot;money money money money,&quot; it's clearly &lt;em&gt;about&lt;/em&gt; money. Repetition means importance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IDF (inverse document frequency)&lt;/strong&gt;: how &lt;em&gt;rare&lt;/em&gt; the word is across the whole pile, measured by how many separate emails contain it. Common word → tiny score. Rare word → big score. (&quot;Inverse&quot; just means flipped: more documents, smaller score.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Multiply them, and something beautiful happens. Take &quot;the&quot;: it appears a lot (high TF) but it's everywhere (near-zero IDF), so high × almost-zero ≈ &lt;strong&gt;zero&lt;/strong&gt;. The word silences itself. Nobody handed the computer a list of words to ignore. The math removed the filler on its own. Meanwhile &quot;viagra&quot; in a spam email is repeated (high TF) &lt;em&gt;and&lt;/em&gt; rare (high IDF), so it lights up bright.&lt;/p&gt;
&lt;p&gt;Same API, one word swapped:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from sklearn.feature_extraction.text import TfidfVectorizer

vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(emails)

print(X.toarray().round(2))  # same shape, but now weighted by rarity
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I tested the idea on &quot;win win win money the the&quot; and correctly predicted &quot;win&quot; would score highest, repeated &lt;em&gt;and&lt;/em&gt; rare, while &quot;the&quot; collapsed to nothing. The method had taught me to think the way it thinks.&lt;/p&gt;
&lt;h3 id=&quot;embeddings-giving-words-a-place-on-a-map&quot;&gt;Embeddings: giving words a place on a map&lt;/h3&gt;
&lt;p&gt;Both methods above share a blindness: to them, &quot;money&quot; and &quot;cash&quot; are as unrelated as &quot;money&quot; and &quot;banana.&quot; Each word is just its own separate column, with nothing connecting them. They have no concept that two different words can mean nearly the same thing.&lt;/p&gt;
&lt;p&gt;Embeddings fix this with one gorgeous idea: put every word on a &lt;strong&gt;map of meaning.&lt;/strong&gt; Picture a giant map where each word is a dot. We place words that mean similar things &lt;em&gt;near each other&lt;/em&gt;, and unrelated words &lt;em&gt;far apart&lt;/em&gt;. &quot;cash&quot; sits right next to &quot;money&quot;; &quot;banana&quot; is way over by &quot;apple.&quot; A word's coordinates on that map, written as numbers, are its &lt;em&gt;embedding.&lt;/em&gt; &quot;king&quot; lands closer to &quot;queen&quot; than to &quot;bicycle,&quot; because the map is organized by meaning.&lt;/p&gt;
&lt;p&gt;But how does a computer that can't read decide where to put each word? Through &lt;strong&gt;the company a word keeps.&lt;/strong&gt; It reads millions of sentences and notices that &quot;money&quot; and &quot;cash&quot; are both surrounded by the same neighbors: borrow, pay, bank, withdraw. Same company, same neighborhood. &quot;banana&quot; keeps different company (eat, peel, ripe, fruit), so it lands far away. The computer never knows what any word &lt;em&gt;means&lt;/em&gt;; it just matches patterns of context. (The real map isn't flat with two directions; it has &lt;em&gt;hundreds&lt;/em&gt;, which is why an embedding is a long list of numbers. The idea is identical: close = similar, far = different.)&lt;/p&gt;
&lt;p&gt;A pre-trained model hands you those coordinates directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from sentence_transformers import SentenceTransformer

model = SentenceTransformer(&quot;all-MiniLM-L6-v2&quot;)
vecs = model.encode([&quot;money&quot;, &quot;cash&quot;, &quot;banana&quot;])

print(vecs.shape)  # (3, 384) → three words, each a dot with 384 coordinates
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The kicker: it's not only single words. A whole sentence, or a whole &lt;em&gt;note&lt;/em&gt;, can become a dot too, and that's the hinge the entire next step swings on.&lt;/p&gt;
&lt;h3 id=&quot;rag-i-accidentally-invented-it&quot;&gt;RAG: I accidentally invented it&lt;/h3&gt;
&lt;p&gt;Here's where it all paid off. Imagine every note in my wiki is a dot on the meaning map. Then I type a question, and &lt;em&gt;that question&lt;/em&gt; becomes a dot on the same map. To find the note that answers it, I just grab the &lt;strong&gt;nearest dots.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That's the entire idea behind RAG (Retrieval-Augmented Generation):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Turn the question into a dot (embed it).&lt;/li&gt;
&lt;li&gt;Find the nearest note-dots on the map (retrieval).&lt;/li&gt;
&lt;li&gt;Hand those notes to a language model and say &quot;answer using only these.&quot;&lt;/li&gt;
&lt;li&gt;The model writes an answer grounded in &lt;em&gt;my actual notes&lt;/em&gt; instead of guessing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most people learn RAG by copying a tutorial and have no idea what's happening underneath. I arrived at it by reasoning, which meant I understood every layer before I wrote a line of code.&lt;/p&gt;
&lt;h3 id=&quot;synapse-making-it-real&quot;&gt;Synapse: making it real&lt;/h3&gt;
&lt;p&gt;So I built it, in a Jupyter notebook, and named it &lt;strong&gt;Synapse&lt;/strong&gt;, because a synapse is the connection between two neurons, which is exactly what embeddings do: connect related notes by meaning.&lt;/p&gt;
&lt;p&gt;The pipeline is just the four ideas above, in order.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Load&lt;/strong&gt; every note:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from pathlib import Path

WIKI = Path(&quot;path/to/my/wiki&quot;)
notes = [{&quot;path&quot;: str(f), &quot;text&quot;: f.read_text(encoding=&quot;utf-8&quot;)}
         for f in WIKI.rglob(&quot;*.md&quot;)]

print(f&quot;Loaded {len(notes)} notes&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2. Embed&lt;/strong&gt; each note into a dot on the map:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from sentence_transformers import SentenceTransformer

model = SentenceTransformer(&quot;all-MiniLM-L6-v2&quot;)
embeddings = model.encode([n[&quot;text&quot;] for n in notes])  # shape (N, 384)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;3. Retrieve&lt;/strong&gt; the nearest notes to a question:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from sklearn.metrics.pairwise import cosine_similarity
import numpy as np

question = &quot;what is the difference between stemming and lemmatization?&quot;
q_emb = model.encode([question])

scores = cosine_similarity(q_emb, embeddings)[0]  # &quot;near = similar&quot;, as a number
top = np.argsort(scores)[::-1][:3]                # the 3 closest notes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;4. Generate&lt;/strong&gt; a grounded answer from those notes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import anthropic

client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY from your environment

context = &quot;\n\n---\n\n&quot;.join(notes[i][&quot;text&quot;] for i in top)
prompt = f&quot;&quot;&quot;Answer the question using ONLY the notes below.
If the answer isn't in the notes, say you don't know.

NOTES:
{context}

QUESTION: {question}&quot;&quot;&quot;

resp = client.messages.create(
    model=&quot;claude-sonnet-4-6&quot;,
    max_tokens=500,
    messages=[{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: prompt}],
)
print(resp.content[0].text)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That last instruction (&lt;em&gt;&quot;if the answer isn't in the notes, say you don't know&quot;&lt;/em&gt;) is the anti-hallucination guard. It's the difference between a toy and something you can trust.&lt;/p&gt;
&lt;p&gt;I asked it: &lt;em&gt;&quot;What's the difference between stemming and lemmatization?&quot;&lt;/em&gt; It pulled up exactly the right notes and wrote an answer that reproduced my own comparison table: the tools, the &quot;studies → studi&quot; example, all of it. Not from the model's training. From &lt;em&gt;my brain&lt;/em&gt;, retrieved and read back to me.&lt;/p&gt;
&lt;h3 id=&quot;what-actually-made-it-click&quot;&gt;What actually made it click&lt;/h3&gt;
&lt;p&gt;An hour into all this, I'd gone from &quot;I understand nothing&quot; to deriving RAG on my own. The lesson wasn't really about NLP. It was about &lt;em&gt;how to learn&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;Reading is not understanding. I'd mistaken having notes for having knowledge. The fix was active discovery: being asked a question and having to reach for the answer, one small step at a time, every abstract idea anchored to something concrete (a paper bag, a map, a spam email). And then &lt;em&gt;building&lt;/em&gt;, because nothing reveals what you don't understand faster than making it run.&lt;/p&gt;
&lt;p&gt;If you're staring at material that won't stick, try this: stop reading it. Have it asked of you instead. Start from the dumbest possible question and refuse to move on until each piece genuinely makes sense. You can get further in a day than you'd believe.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Next up for Synapse: chunking notes for sharper retrieval, citing its sources, a real frontend, and a local-model version. The journey continues. I'll write that one up too.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22742/from-i-understood-nothing-to-building-a-rag-app</guid>
<pubDate>Wed, 15 Jul 2026 09:10:21 +0000</pubDate>
</item>
<item>
<title>How to Start Docker for Beginners</title>
<link>https://coderlegion.com/22690/how-to-start-docker-for-beginners</link>
<description>&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/1t3vukgnuk2r4754axw6.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-s-docker&quot;&gt;What's Docker&lt;/h2&gt;
&lt;blockquote&gt;&lt;p&gt;Docker is a containerization platform that packages applications with all dependencies into portable utils called containers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;learn-the-foundational-concepts-of-docker&quot;&gt;Learn the foundational concepts of &lt;code&gt;Docker&lt;/code&gt;&lt;/h2&gt;
&lt;h3 id=&quot;core-concepts-&quot;&gt;Core concepts:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Image&lt;/strong&gt;: A read-only blueprint containing app code,libraries,and environment settings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Container&lt;/strong&gt;: A running instance of an image.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;: Script defining how to build an image.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Volume&lt;/strong&gt;: Persistent storage outside the container lifecycle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network&lt;/strong&gt;: Enables communication between containers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;example-building-and-running-a-container&quot;&gt;&lt;strong&gt;Example: Building and Running a Container&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;FROM node:18
WORKDIR /app
COPY package.json ./
RUN npm install
COPY .
EXPOSE 3000
CMD [&quot;npm&quot;, &quot;start&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker build -t my-node-app

docker run -p 3000:3000 my-node-app
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;managing-multi-container-apps-with-docker-compose&quot;&gt;Managing Multi-Container Apps with Docker Compose&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;version: '3'
services:
 app:
   build:
   ports:
     - &quot;3000:3000&quot;
   depends_on:
     - mongo
 mongo:
   image: mongo
   volumes:
     - mongo-data:/data/db
volumes:
 mongo-data:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;RUN with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;best-practices&quot;&gt;Best Practices&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;.dockerignore&lt;/code&gt; to exclude unnecessary files(e.g.,&lt;code&gt;.git&lt;/code&gt;,&lt;code&gt;node_modules&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Avoid &lt;code&gt;:latest&lt;/code&gt; tags;pin versions for stability.&lt;/li&gt;
&lt;li&gt;Run containers as non-root for security.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;multi-stage builds&lt;/strong&gt; to reduce image size.&lt;/li&gt;
&lt;li&gt;Store secrets in &lt;code&gt;.env&lt;/code&gt; files or Docker secrets, not in images.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-to-use-docker&quot;&gt;When to Use Docker&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[Y] Consistent dev/prod environments&lt;/li&gt;
&lt;li&gt;[N] Small static sites or quick scripts where setup overhead outweighs benefits.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next Steps&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Learn &lt;strong&gt;Docker networking&lt;/strong&gt;(bridge, host, overlay)for container communication.&lt;/li&gt;
&lt;li&gt;Explore &lt;strong&gt;Docker volumes&lt;/strong&gt; for persistent data.&lt;/li&gt;
&lt;li&gt;Integrate Docker into &lt;strong&gt;CI/CD pipelines&lt;/strong&gt; for automated builds and deployments.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;With these fundamentals, you can confidently containerize, run, and manage applications in any environment.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22690/how-to-start-docker-for-beginners</guid>
<pubDate>Wed, 15 Jul 2026 00:54:55 +0000</pubDate>
</item>
<item>
<title>What's the Future of Coding Practice Platforms in the AI Era?</title>
<link>https://coderlegion.com/22231/whats-the-future-of-coding-practice-platforms-in-the-ai-era</link>
<description>&lt;p&gt;We all seem to agree on one thing: the best way to learn a new language or framework today is to build real projects. Tutorials and exercises only take you so far.&lt;/p&gt;
&lt;p&gt;But that got me thinking about platforms like LeetCode, HackerRank or Exercism, the ones built around coding challenges, algorithms, and exercises.&lt;/p&gt;
&lt;p&gt;In a world where AI can solve most of those challenges in seconds, what's their future?&lt;br&gt;
Are they still relevant as learning tools? Or are they slowly becoming obsolete replaced by project-based learning and AI-assisted exploration?&lt;/p&gt;
&lt;p&gt;I'm also curious about the hiring side: companies have used LeetCode-style interviews for years. Does that change when candidates can just... ask AI?&lt;/p&gt;
&lt;p&gt;A few questions I'd love your take on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do you still use platforms like LeetCode or Exercism to practice?&lt;/li&gt;
&lt;li&gt;Do you think algorithm challenges are still a valuable way to learn in 2026?&lt;/li&gt;
&lt;li&gt;Should these platforms reinvent themselves and if so, how?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Would love to hear from people who've used these platforms recently, or who hire developers and still rely on this kind of assessment. 👇&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/22231/whats-the-future-of-coding-practice-platforms-in-the-ai-era</guid>
<pubDate>Fri, 10 Jul 2026 16:47:13 +0000</pubDate>
</item>
<item>
<title>First miniconference on raytracing and GPUs as concepts in computer graphics </title>
<link>https://coderlegion.com/21884/first-miniconference-on-raytracing-and-gpus-as-concepts-in-computer-graphics</link>
<description>&lt;p&gt;Yesterday, I attended a mini conference at zone01 Kisumu that focused on ray tracing, GPUs, and the technology behind modern computer graphics.&lt;/p&gt;
&lt;p&gt;Before the session, I had heard these terms many times but didn't fully understand how they worked together. The talks helped me connect the dots.&lt;/p&gt;
&lt;p&gt;Some of the key things I learned include:&lt;/p&gt;
&lt;p&gt;What ray tracing is: An advanced rendering technique that simulates how light behaves to create realistic shadows, reflections, and lighting.&lt;/p&gt;
&lt;p&gt;Why GPUs are important: Unlike CPUs, GPUs are designed to process thousands of calculations simultaneously, making them ideal for graphics rendering and other highly parallel tasks.&lt;/p&gt;
&lt;p&gt;How ray tracing uses GPU power: Since ray tracing requires a huge number of calculations, modern GPUs accelerate the process, making real-time rendering possible in games and other applications.&lt;/p&gt;
&lt;p&gt;Real-world applications: Beyond gaming, ray tracing is used in animation, architecture, engineering, product design, and visual effects.&lt;/p&gt;
&lt;p&gt;One of the biggest takeaways for me was seeing how computer science concepts like parallel processing have practical applications in creating realistic digital experiences.&lt;/p&gt;
&lt;p&gt;As someone still learning and growing as a developer, I enjoyed the session as it was a great learning opportunity for me that introduced me to concepts I didn't know about. &lt;/p&gt;
&lt;p&gt;A big thank you to the organizers and speakers at Zone01 for sharing their knowledge. Looking forward to attending more technical sessions like this.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21884/first-miniconference-on-raytracing-and-gpus-as-concepts-in-computer-graphics</guid>
<pubDate>Sat, 04 Jul 2026 06:33:38 +0000</pubDate>
</item>
<item>
<title>My First Global Hackathon: What I Learned Building Moonsu Link</title>
<link>https://coderlegion.com/21863/my-first-global-hackathon-what-i-learned-building-moonsu-link</link>
<description>&lt;p&gt;This was my first global hackathon, with 500+ participants from around the world, and although I didn’t win, I’m honestly proud of how much I learned from the experience.&lt;/p&gt;
&lt;p&gt;I worked on &lt;strong&gt;Moonsu Link&lt;/strong&gt;, a project I care about deeply. It wasn’t just about building something technical. It was about trying to solve a real problem and pushing an unfinished idea all the way into something more complete, more usable, and more meaningful.&lt;/p&gt;
&lt;p&gt;Like many hackathon projects, Moonsu Link came with its own challenges. There were moments of uncertainty, moments where the product needed more structure, and moments where it would have been easy to slow down or stop. But I kept going because I believed the idea was worth finishing.&lt;/p&gt;
&lt;h3 id=&quot;the-power-of-collaboration-amp-community&quot;&gt;The Power of Collaboration &amp;amp; Community&lt;/h3&gt;
&lt;p&gt;What made this experience special was the teamwork. Building with others, sharing ideas, dividing responsibilities, and trying to keep the momentum alive taught me a lot about collaboration. A hackathon is not just about code. It is also about communication, trust, iteration, and resilience.@alchemycodes @yvesdylane &lt;/p&gt;
&lt;p&gt;I also learned that progress matters, even when the final result is not a win. Finishing something, improving it, and learning through the process can be just as valuable as a prize.&lt;/p&gt;
&lt;p&gt;One of the things I’m happy about is that I received a badge from the DEVPost community. That recognition means a lot to me because it reflects the effort, growth, and persistence behind the project.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;key-takeaways-2&quot;&gt;Key Takeaways&lt;/h3&gt;
&lt;p&gt;This hackathon reminded me that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;First attempts are still valuable&lt;/strong&gt; – Every expert was once a beginner. Starting is half the battle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teamwork makes a huge difference&lt;/strong&gt; – Bouncing ideas off others and distributing tasks accelerates growth.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Challenges are part of the process&lt;/strong&gt; – Friction and bugs are inevitable; navigating them builds engineering maturity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Not winning does not mean losing&lt;/strong&gt; – The knowledge and connections gained are permanent assets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learning and finishing count&lt;/strong&gt; – Shipping a complete product cycle is a victory in itself.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;moving-forward&quot;&gt;Moving Forward&lt;/h3&gt;
&lt;p&gt;Moonsu Link may not have taken the top prize, but it gave me something important: experience, confidence, and a stronger mindset for the next challenge. &lt;/p&gt;
&lt;p&gt;I’m grateful for the opportunity, grateful for the people I worked with, and grateful that I chose not to give up. &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Have you ever participated in a global hackathon? What was your biggest takeaway from your first dev challenge? Let's connect in the comments below!&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21863/my-first-global-hackathon-what-i-learned-building-moonsu-link</guid>
<pubDate>Sat, 04 Jul 2026 02:55:05 +0000</pubDate>
</item>
<item>
<title>The Reality of Roadmaps 🗺️</title>
<link>https://coderlegion.com/20091/the-reality-of-roadmaps</link>
<description>&lt;blockquote&gt;&lt;p&gt;Do roadmaps actually help people learn? &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am not sure.&lt;/p&gt;
&lt;p&gt;Because the first time I saw one, it did the exact opposite.&lt;/p&gt;
&lt;p&gt;I remember opening a DevOps roadmap at roadmap.sh and seeing Linux, Networking, AWS, Docker, Terraform, Kubernetes, CI/CD, Security...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqfjplbs7un3oenuqh7s.png&quot; alt=&quot;Learning roadmap&quot;&gt;&lt;/p&gt;
&lt;p&gt;The list just kept going.&lt;/p&gt;
&lt;p&gt;Instead of feeling motivated, I felt overwhelmed.&lt;/p&gt;
&lt;p&gt;I closed all the tabs, shut down my laptop, and went to sleep.&lt;/p&gt;
&lt;p&gt;I am serious.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyze85bwsagrbsafhi73.png&quot; alt=&quot;Shut-down&quot;&gt;&lt;/p&gt;
&lt;p&gt;It looked less like a learning path and more like a university degree.&lt;/p&gt;
&lt;p&gt;What made it worse was that I went back the next day and tried again.&lt;/p&gt;
&lt;p&gt;Same feeling.&lt;/p&gt;
&lt;p&gt;Surely there had to be a simpler version somewhere.&lt;/p&gt;
&lt;p&gt;So I did what most people do.&lt;/p&gt;
&lt;p&gt;I went looking for a better roadmap.&lt;/p&gt;
&lt;p&gt;Instead, I found more roadmaps with different layouts, orders, and opinions.&lt;/p&gt;
&lt;p&gt;One said to learn Docker before Kubernetes.&lt;/p&gt;
&lt;p&gt;Another said the opposite.&lt;/p&gt;
&lt;p&gt;One started with Python.&lt;/p&gt;
&lt;p&gt;Another skipped it entirely.&lt;/p&gt;
&lt;p&gt;I kept searching for the version that would finally make sense.&lt;/p&gt;
&lt;p&gt;What I found instead was more confusion.&lt;/p&gt;
&lt;p&gt;At some point, I stopped and asked myself an honest question.&lt;/p&gt;
&lt;p&gt;Was I actually learning anything?&lt;/p&gt;
&lt;p&gt;The answer was no.&lt;/p&gt;
&lt;p&gt;I was researching how to learn without doing any learning.&lt;/p&gt;
&lt;p&gt;Collecting roadmaps as if they were going to do the learning for me.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jfg43nihmb8tm7s0bp5j.png&quot; alt=&quot;Learning stack&quot;&gt;&lt;/p&gt;
&lt;p&gt;That realisation was uncomfortable.&lt;/p&gt;
&lt;p&gt;Because it meant the problem was not the roadmap.&lt;/p&gt;
&lt;p&gt;It was what I expected the roadmap to do.&lt;/p&gt;
&lt;p&gt;There are AWS concepts that took me weeks to understand.&lt;/p&gt;
&lt;p&gt;There are things I thought I understood until I tried using them.&lt;/p&gt;
&lt;p&gt;There are topics I ignored and had to come back to later.&lt;/p&gt;
&lt;p&gt;None of that ever shows up on a roadmap.&lt;/p&gt;
&lt;p&gt;The roadmap only shows the clean version of the story.&lt;/p&gt;
&lt;p&gt;The finished version.&lt;/p&gt;
&lt;p&gt;The version that makes sense after everything has already happened.&lt;/p&gt;
&lt;p&gt;Roadmaps show what a field looks like when you zoom out.&lt;/p&gt;
&lt;p&gt;They do not show what learning feels like when you are inside it.&lt;/p&gt;
&lt;p&gt;I still visit roadmap.sh.&lt;/p&gt;
&lt;p&gt;I still think roadmaps are useful.&lt;/p&gt;
&lt;p&gt;But I no longer expect them to remove the messy part of learning.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h3hmos3ffp7w0e1w06la.png&quot; alt=&quot;Feasible/reality roadmap&quot;&gt;&lt;/p&gt;
&lt;p&gt;This roadmap feels real to me.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you enjoy reflections like this, you can subscribe to the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7336744982432600064&quot;&gt;newsletter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can follow my work and projects on &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/ijaydimples&quot;&gt;Twitter&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/ijeoma-igboagu/&quot;&gt;LinkedIn&lt;/a&gt;, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ijayhub&quot;&gt;GitHub&lt;/a&gt;, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/@cloudinreallife&quot;&gt;YouTube&lt;/a&gt;, where I share what I am building and learning along the way.&lt;/p&gt;
&lt;p&gt;Thank you for reading.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20091/the-reality-of-roadmaps</guid>
<pubDate>Tue, 30 Jun 2026 09:00:00 +0000</pubDate>
</item>
</channel>
</rss>