<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[AheadKit]]></title><description><![CDATA[AheadKit (aheadkit.me) publishes plain-English programming books for absolute beginners — Python, JavaScript, C++, Java, SQL and more, from $2.99, with free chapters to read before you buy.]]></description><link>https://aheadkit.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6ae4ec5d4e14ac3dd14cd7fc/19ffbaa5-1ea5-44d3-87de-38872ccdcf94.png</url><title>AheadKit</title><link>https://aheadkit.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 07 Nov 2026 11:48:08 GMT</lastBuildDate><atom:link href="https://aheadkit.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Why My Cron Job Ran Twice: A Race Condition in Two Lines of Code]]></title><description><![CDATA[A cron job ran twice last Tuesday. Not a crash, not an error — the same job, twice, four minutes apart, doing the same work on the same data. The second…]]></description><link>https://aheadkit.hashnode.dev/why-my-cron-job-ran-twice-a-race-condition-in-two-lines-of-code</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/why-my-cron-job-ran-twice-a-race-condition-in-two-lines-of-code</guid><category><![CDATA[backend]]></category><category><![CDATA[debugging]]></category><category><![CDATA[cron]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Sat, 07 Nov 2026 06:21:46 GMT</pubDate><content:encoded><![CDATA[<p>A cron job ran twice last Tuesday. Not a crash, not an error — the same job, twice, four minutes apart, doing the same work on the same data. The second run sent duplicate emails to a handful of users before I noticed. The cause was two lines of code and a wrong assumption about how cron works. Here is the whole story, because this bug is common, quiet, and easy to prevent.</p>
<h2>The setup</h2>
<p>The job was simple: every hour, find users whose trial expired in the last hour and send them a reminder email. The code looked like this:</p>
<pre><code class="language-js">async function sendTrialReminders() {
  const users = await db.users.findMany({
    where: { trialEndsAt: { lte: new Date() }, reminderSent: false },
  });
  for (const user of users) {
    await sendEmail(user, "Your trial has ended");
    await db.users.update({
      where: { id: user.id },
      data: { reminderSent: true },
    });
  }
}
</code></pre>
<p>Find users who need a reminder, send it, mark it sent. Read, act, mark. What could go wrong?</p>
<h2>The two lines that matter</h2>
<p>The bug lives in the gap between these two lines:</p>
<pre><code class="language-js">const users = await db.users.findMany({ ... reminderSent: false });
</code></pre>
<p>and</p>
<pre><code class="language-js">await db.users.update({ ... data: { reminderSent: true } });
</code></pre>
<p>Between finding a user and marking them, time passes. Sending an email takes a second or two. With fifty users, the loop takes a couple of minutes. And cron, meanwhile, does exactly what cron does: it runs the job again on schedule, whether the previous run finished or not.
On Tuesday the email service was slow. The first run took longer than usual. The second run started while the first was still working through its list. The second run queried the same <code>reminderSent: false</code> rows — because the first run had not marked them yet — and sent the same emails again.
That is a race condition: two runs of the same job, both reading the same unmarked rows, both acting on them. No error anywhere. Both runs completed successfully. The users got two emails.</p>
<h2>Why I assumed it could not happen</h2>
<p>I had assumed, without ever checking, that cron jobs do not overlap. That assumption felt safe because it usually is: a job that takes two minutes, scheduled hourly, never overlaps. Until the day it takes sixty-five minutes, or the email service slows down, or the database stalls, and suddenly the schedule and the runtime collide. "Usually finishes in time" is not a guarantee. It is a hope, and hopes do not hold under load.</p>
<h2>The fix</h2>
<p>The fix has two parts, and you want both.
<strong>1. Claim the work before you do it.</strong> Mark the rows as claimed in the same step that finds them, atomically, so a second run cannot see them:</p>
<pre><code class="language-js">const claimed = await db.users.updateMany({
  where: { trialEndsAt: { lte: new Date() }, reminderSent: false },
  data: { reminderSent: true },
});
// then fetch the claimed rows and send the emails
</code></pre>
<p><code>updateMany</code> is a single atomic operation in the database. Two runs cannot both claim the same row. The second run finds nothing to do and exits quietly. The marking happens before the slow work, not after it.
Note the trade-off: if the email send fails after claiming, the user is marked as reminded without receiving anything. For a reminder email, that is acceptable — the user can be re-targeted by a separate retry pass. For a payment or a critical notification, you want a three-state flag instead: <code>pending</code>, <code>sent</code>, <code>failed</code>, with a retry job that picks up <code>failed</code> rows. The principle is the same: the state change that prevents a duplicate must happen atomically, before the slow work.
<strong>2. Lock the job itself.</strong> Even with atomic claiming, add a lock so two runs cannot execute at the same time. The simplest version is a lock row in the database with an expiry: the job writes "running, locked until X" at the start, and refuses to start if a live lock exists. If a run crashes, the lock expires and the next run proceeds. Libraries exist for this in every ecosystem, and most of them are a dozen lines. Use one.</p>
<h2>The check that finds this bug before users do</h2>
<p>Look at every scheduled job you run and ask two questions:</p>
<ol>
<li>If this job runs twice at the same time, what happens? If the answer is "duplicate work", it needs an atomic claim or a lock.</li>
<li>If this job runs longer than its schedule interval, does the scheduler start a second copy? For cron, the answer is yes, always. For most job queues, the answer depends on configuration — check yours.
The bug cost me a handful of duplicate emails and an apology. It could have cost duplicate charges, duplicate database writes, duplicate anything. The fix was one atomic update and a lock — less code than the apology email I had to write.</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[The Node.js TLS Bug That Broke Certificate Checks — and How to Tell If You're Affected]]></title><description><![CDATA[A critical TLS certificate-validation vulnerability was disclosed in Node.js 24.x and 26.x, tracked as CVE-2026-105483. It is the kind of bug that sounds…]]></description><link>https://aheadkit.hashnode.dev/the-node-js-tls-bug-that-broke-certificate-checks-and-how-to-tell-if-you-re-affected</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/the-node-js-tls-bug-that-broke-certificate-checks-and-how-to-tell-if-you-re-affected</guid><category><![CDATA[Node.js]]></category><category><![CDATA[Security]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TLS]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Sat, 07 Nov 2026 06:19:44 GMT</pubDate><content:encoded><![CDATA[<p>A critical TLS certificate-validation vulnerability was disclosed in Node.js 24.x and 26.x, tracked as CVE-2026-105483. It is the kind of bug that sounds abstract until you translate it: under certain conditions, a Node server or client could accept a TLS certificate it should have rejected. Here is what the bug is, in plain terms, who is affected, and how to check your own setup.</p>
<h2>What the bug is</h2>
<p>TLS certificate validation is the check that answers one question: is the server I am talking to actually the server it claims to be? When your Node app connects to an API over HTTPS, it checks the server's certificate against a list of trusted authorities and against the hostname it expected. If the certificate does not match, the connection is refused.
CVE-2026-105483 is a flaw in that validation logic in Node.js 24.x and 26.x. In the affected versions, certain malformed or mismatched certificates could pass validation when they should have failed. The practical consequence is that an attacker who can sit between your app and the server it talks to — a compromised network, a hostile Wi-Fi access point, a DNS trick — could present a certificate that is not valid for that hostname and still have the connection accepted.
That is a serious failure, because certificate validation is the one thing standing between "encrypted connection to the right server" and "encrypted connection to whoever is listening."</p>
<h2>Who is affected</h2>
<p>The affected lines are Node.js 24.x and 26.x. If you run either of those in production, you are in scope. Other lines are not affected by this specific vulnerability, which is worth stating plainly: this is not a "Node is broken" story. It is a bug in two release lines, with a fix in the patched releases.
What makes it worse than a typical dependency bug is where it sits. This is not a package you can update with <code>npm update</code>. It is the runtime itself. Every HTTPS request your app makes, and every TLS connection your server accepts, goes through this code.</p>
<h2>How to tell if you're affected</h2>
<p>Run one command on every machine and container that runs your app:</p>
<pre><code class="language-bash">node --version
</code></pre>
<p>If the output starts with <code>v24.</code> or <code>v26.</code>, check the exact patch version against the fixed releases in the Node.js security advisory. Anything below the fixed version in those lines needs the upgrade. If you run <code>v20.</code> or <code>v22.</code> or anything else, this CVE does not apply to you — but check anyway, because the next one might.
Then check the places version numbers hide:</p>
<ul>
<li><strong>Docker images.</strong> Your <code>Dockerfile</code> probably says <code>node:24-alpine</code> or similar. That tag moves, but the image you built last month does not. Rebuild the image after the base image updates, and check the version inside the running container, not the tag on the outside.</li>
<li><strong>CI runners and deployment targets.</strong> The Node version in your CI config and the one on your server are often different. Check both. A patched CI and an unpatched server is still an unpatched server.</li>
<li><strong>Bundled runtimes.</strong> Electron apps, serverless layers, and tools that ship their own Node can carry an affected version without it appearing in your <code>package.json</code>. If a tool bundles Node, check the tool's release notes.</li>
</ul>
<h2>The fix</h2>
<p>Upgrade Node to a patched release in the 24.x or 26.x line. There is no configuration workaround worth relying on — certificate validation is not something to patch around with a flag. The upgrade is the fix.
After upgrading, verify rather than assume. Run <code>node --version</code> on the deployed machine and confirm the number changed. Then run your TLS-dependent test suite — the tests that hit real HTTPS endpoints — and confirm they still pass. A runtime upgrade that silently changes TLS behaviour is exactly the kind of change that deserves a test run, not a shrug.</p>
<h2>The habit this should build</h2>
<p>Most teams pin their dependencies carefully and let the runtime float. The runtime is the biggest dependency you have, and it changes the least often, which is why it gets the least attention. Put the Node version in the same place you put your dependency checks: a scheduled check, a pinned version in your Dockerfile, and a habit of reading the Node.js security advisories the way you read your package audit output.
CVE-2026-105483 will be a footnote in a month. The habit of knowing exactly which runtime version is running in production will keep paying off long after the CVE is forgotten.</p>
]]></content:encoded></item><item><title><![CDATA[Go's New JSON Package: What Changed and Why It Matters]]></title><description><![CDATA[Go has shipped a reworked JSON package, json/v2, and it is the kind of change that looks boring in a changelog and matters a lot in real code. I spent an…]]></description><link>https://aheadkit.hashnode.dev/go-s-new-json-package-what-changed-and-why-it-matters</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/go-s-new-json-package-what-changed-and-why-it-matters</guid><category><![CDATA[Go Language]]></category><category><![CDATA[json]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[backend]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Sat, 07 Nov 2026 06:17:41 GMT</pubDate><content:encoded><![CDATA[<p>Go has shipped a reworked JSON package, <code>json/v2</code>, and it is the kind of change that looks boring in a changelog and matters a lot in real code. I spent an evening reading the proposal and porting a small service to it. Here is what actually changed, what breaks, and whether you should care yet.</p>
<h2>Why a new JSON package at all</h2>
<p>The old <code>encoding/json</code> is one of Go's oldest packages, and it shows. It was designed before generics, before the community settled on how JSON should behave at the edges, and it carries two decades of backwards-compatibility promises. The result is a package that works fine for the common case and surprises you at the edges: numbers that lose precision, struct tags that silently do nothing when misspelled, error messages that tell you something failed but not where.
<code>json/v2</code> is a fresh start under the <code>encoding/json/v2</code> import path. It keeps the shape you know — <code>Marshal</code>, <code>Unmarshal</code>, struct tags — and fixes the sharp edges. The old package is not going away. The new one is the future, and it is designed so the two can coexist while you migrate piece by piece.</p>
<h2>What changed</h2>
<p><strong>Numbers keep their precision.</strong> The old package decodes every JSON number into a <code>float64</code> by default, which quietly mangles large integers — a 64-bit ID like <code>9007199254740993</code> comes back as <code>9007199254740992</code>. The new package preserves integer precision by default. If your service passes IDs or money amounts through JSON, this alone is worth the migration.
<strong>Struct tags are validated.</strong> In the old package, a typo in a struct tag — <code>json:"usernmae"</code> — fails silently. The field just never matches, and you find out in production when a field is mysteriously empty. The new package reports unknown or malformed tag options as errors. A typo becomes a compile-time-style failure instead of a silent data loss bug.
<strong>Errors say where.</strong> The old package's errors are famously vague: "invalid character 'x' looking for beginning of value". The new package's errors include the position in the input and the path to the value that failed — which field, nested how deep. Debugging a bad payload goes from reading the whole document to reading one line.
<strong>Streaming and options are cleaner.</strong> The new API separates the common operations from the configuration. Instead of a bag of methods on <code>Encoder</code> and <code>Decoder</code>, options are explicit values you pass in, which makes it easier to see what a given call actually does. The <code>jsontext</code> sub-package exposes the low-level token stream for the cases where you need it — building a JSON linter, say, or transforming a document without decoding it fully.</p>
<h2>What breaks</h2>
<p>This is a new package, not a drop-in replacement, and the migration has real edges:</p>
<ul>
<li><strong>Import path changes.</strong> You import <code>encoding/json/v2</code> instead of <code>encoding/json</code>. Both can live in the same program, which is good for gradual migration and confusing for exactly one afternoon.</li>
<li><strong>Default behaviours differ.</strong> The number-precision change is a fix, but it is also a behaviour change: code that relied on float64 rounding will see different values. The stricter tag validation will surface typos you have been shipping for years — which is the point, but it means the first build after migrating may fail in places you did not expect.</li>
<li><strong>Some old escape hatches are gone.</strong> A few of the old package's quirks that people worked around — custom <code>MarshalJSON</code> patterns that depended on specific error text, for instance — will not behave identically. Read the migration notes before porting a hot path.</li>
</ul>
<h2>Should you migrate now</h2>
<p>For a new service: yes, start with <code>json/v2</code>. The defaults are better, the errors are better, and you avoid building on the package the language itself is moving away from.
For an existing service: migrate the edges first. Start with the code that handles money, IDs, or untrusted input — the places where silent number mangling and silent tag typos cost real money. Leave the happy-path CRUD endpoints on the old package until you have a reason to touch them. The two packages coexist, so there is no big-bang rewrite and no deadline.
One caution: <code>json/v2</code> is new, and new means the ecosystem around it — linters, code generators, libraries that wrap <code>encoding/json</code> — is still catching up. If a library you depend on does its own JSON handling through the old package, mixing the two in the same request path can produce subtle inconsistencies. Check your dependencies before you migrate their inputs.</p>
<h2>The takeaway</h2>
<p>Go's JSON story for years was "it works, mostly, except when it quietly doesn't." The new package closes the quiet failure modes: numbers stay exact, typos get caught, errors point at the problem. It is not exciting. It is the kind of change that prevents a 2am incident six months from now, which is the best kind of change a standard library can ship.</p>
]]></content:encoded></item><item><title><![CDATA[MCP Servers, Explained by Building One for My Own Notes]]></title><description><![CDATA[Model Context Protocol — MCP — is the piece of the AI-tools puzzle that sounds complicated and turns out to be simple once you build one. I built a tiny…]]></description><link>https://aheadkit.hashnode.dev/mcp-servers-explained-by-building-one-for-my-own-notes</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/mcp-servers-explained-by-building-one-for-my-own-notes</guid><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Tutorial]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Sat, 07 Nov 2026 06:13:39 GMT</pubDate><content:encoded><![CDATA[<p>Model Context Protocol — MCP — is the piece of the AI-tools puzzle that sounds complicated and turns out to be simple once you build one. I built a tiny MCP server for my own notes folder last weekend, in about an hour, and it taught me more about the protocol than a week of reading about it. Here is the whole thing, explained through the code.</p>
<h2>What MCP actually is</h2>
<p>Strip away the branding and MCP is a client-server protocol with three ideas:</p>
<ul>
<li><strong>Tools</strong>: functions the AI can call. "Search my notes", "read this file", "add a task".</li>
<li><strong>Resources</strong>: data the AI can read. Your notes, your docs, your database rows.</li>
<li><strong>Prompts</strong>: reusable templates. "Summarise today's notes" as a saved starting point.
The AI client (Claude Desktop, an IDE, whatever you use) connects to your server, asks what tools it has, and calls them when the conversation needs them. Your server is just a program that answers those calls. That is the whole protocol. Everything else is packaging.</li>
</ul>
<h2>The server I built</h2>
<p>My notes live in a folder of Markdown files. I wanted the AI to search them and read them, so I never have to paste a note into a chat window again. The server exposes two tools: <code>search_notes</code> and <code>read_note</code>.
I used the TypeScript SDK. The skeleton is this:</p>
<pre><code class="language-ts">import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "notes", version: "1.0.0" });
server.tool(
  "search_notes",
  "Search Markdown notes by keyword and return matching filenames",
  { query: z.string() },
  async ({ query }) =&gt; {
    const matches = searchFiles(NOTES_DIR, query);
    return { content: [{ type: "text", text: matches.join("\n") }] };
  }
);
</code></pre>
<p>That is most of it. A tool is a name, a description, a schema for its inputs (written with Zod), and a handler function that returns text. The description matters more than people think: the AI reads it to decide when to call the tool, so write it the way you would explain the tool to a colleague.
The transport is stdio — the server reads JSON-RPC messages from stdin and writes replies to stdout. No HTTP, no port, no deployment. The client launches the server as a subprocess. That sounds primitive, and it is, and that is why it works everywhere.</p>
<h2>The part that surprised me</h2>
<p>Two things surprised me.
First, how little code the protocol needs. My whole server is under 150 lines, and most of that is the file-search logic, not MCP. The protocol gets out of the way. The SDK handles the handshake, the tool listing, the schema validation. You write the function; MCP delivers the call.
Second, how much the description shapes behaviour. My first description for <code>search_notes</code> was "Search notes". The AI called it for everything, including questions that had nothing to do with my notes. I changed it to "Search the user's personal Markdown notes by keyword — use only when the question is about something the user has written down before" and the false calls stopped. The description is the API documentation and the usage policy in one sentence. Write it carefully.</p>
<h2>The read tool, and one safety rule</h2>
<pre><code class="language-ts">server.tool(
  "read_note",
  "Read one Markdown note by filename and return its full text",
  { filename: z.string() },
  async ({ filename }) =&gt; {
    const safe = path.basename(filename);
    const text = await fs.readFile(path.join(NOTES_DIR, safe), "utf-8");
    return { content: [{ type: "text", text }] };
  }
);
</code></pre>
<p>Notice the <code>path.basename</code> call. The AI supplies the filename, and the AI can be tricked — by a poisoned note, by a pasted message — into asking for <code>../../secrets.txt</code>. Stripping the path down to a bare filename is one line, and it is the difference between a tool and a hole. Every MCP tool that touches the filesystem needs this. Do not skip it because your server is "just for personal use". Personal-use tools are the ones that end up connected to everything.</p>
<h2>Wiring it up</h2>
<p>The client configuration is a JSON file pointing at the server command:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "notes": {
      "command": "node",
      "args": ["/path/to/notes-server.js"]
    }
  }
}
</code></pre>
<p>Restart the client, and the two tools appear. Ask "what did I write about commit messages last month" and the AI calls <code>search_notes</code>, then <code>read_note</code>, then answers from your own words. It feels like magic the first time. It is not magic. It is a function call with a good description.</p>
<h2>When to build one, and when not to</h2>
<p>Build an MCP server when you have data or tools the AI keeps asking you to paste in by hand: notes, a local database, a CLI you run ten times a day. If the same paste happens three times, a tool is cheaper than the pasting.
Do not build one for things the client already does well. Web search, general knowledge, code generation — the model has those. MCP is for your stuff: your files, your systems, your data. That is where it earns its keep.
An hour, 150 lines, two tools. My notes are now searchable by conversation, and I finally understand the protocol, because I built the smallest possible version of it instead of reading about the biggest.</p>
]]></content:encoded></item><item><title><![CDATA[How I Debugged a Flaky Test That Only Failed on CI]]></title><description><![CDATA[There is a special kind of dread that comes with a test that passes on your machine and fails on CI. Not always — sometimes. The worst word in testing is…]]></description><link>https://aheadkit.hashnode.dev/how-i-debugged-a-flaky-test-that-only-failed-on-ci</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/how-i-debugged-a-flaky-test-that-only-failed-on-ci</guid><category><![CDATA[Testing]]></category><category><![CDATA[debugging]]></category><category><![CDATA[ci]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Sat, 07 Nov 2026 06:11:34 GMT</pubDate><content:encoded><![CDATA[<p>There is a special kind of dread that comes with a test that passes on your machine and fails on CI. Not always — sometimes. The worst word in testing is "sometimes", because a test that fails sometimes teaches you nothing, and you cannot fix what you cannot reproduce.
I had one. A test for a date-formatting helper, green locally for weeks, red on CI about one run in five. Here is how I found it, and the two habits that stop this class of bug from coming back.</p>
<h2>The symptom</h2>
<p>The test was simple:</p>
<pre><code class="language-js">test("formats a timestamp as HH:MM", () =&gt; {
  const result = formatTime(new Date("2026-11-07T09:30:00"));
  expect(result).toBe("09:30");
});
</code></pre>
<p>On CI it failed with <code>Expected "09:30", received "09:29"</code> or sometimes <code>"09:31"</code>. Off by a minute, in either direction. Locally, never. The test had no randomness, no network, no database. Just a date and a string.</p>
<h2>The first wrong theory</h2>
<p>My first guess was timezones. CI runners often run in UTC while my laptop runs in local time, and date bugs love that gap. I checked: the runner's timezone was UTC, mine was +06:00, and the test used a fixed date string. But the expected value "09:30" was written against the local reading of that date, so in UTC the test should have failed every time, not one time in five. Wrong theory, but it taught me something: intermittent failures that depend on the clock are almost always about the clock.</p>
<h2>Reproducing it</h2>
<p>The trick with a flaky test is to stop asking "why does it fail" and start asking "what is different between the runs that pass and the runs that fail." I added one line to the CI job: print the current time at the start of the test run.
The pattern jumped out immediately. Every failing run started within a few seconds of a minute boundary. Every passing run started mid-minute. The test was not flaky. It was a race against the clock, and the clock was winning about one run in five — exactly as often as a test run happens to straddle a minute change.
Here is the mechanism. My <code>formatTime</code> helper did this:</p>
<pre><code class="language-js">function formatTime(date) {
  const now = new Date();
  // ... some logic that compared `date` against `now`
  // and rounded to the nearest minute
}
</code></pre>
<p>The function read the current time internally, and the test compared its output against a fixed expectation. If the test started at 09:30:58 and the function ran at 09:31:01, the rounding tipped over the boundary and the answer changed. Locally my runs were fast and I never straddled a boundary. On CI, the runner was slower, the test suite was bigger, and the boundary got crossed regularly.</p>
<h2>The fix</h2>
<p>Two changes, both small:
<strong>1. Inject the clock.</strong> The helper now takes the current time as a parameter with a default, instead of reading it internally:</p>
<pre><code class="language-js">function formatTime(date, now = new Date()) {
  // ... same logic, but `now` is passed in
}
</code></pre>
<p>The test passes a fixed <code>now</code>. The function is deterministic. The same input always gives the same output, on every machine, at every second.
<strong>2. Freeze time in the test.</strong> For tests that genuinely need "now", use a fake timer. In Jest that is <code>jest.useFakeTimers().setSystemTime(new Date("2026-11-07T09:30:00"))</code>. In Vitest, <code>vi.useFakeTimers()</code> and <code>vi.setSystemTime(...)</code>. The test controls the clock, so the clock can never surprise it.</p>
<h2>The habits that prevent the next one</h2>
<p><strong>Treat any use of <code>new Date()</code>, <code>Date.now()</code>, <code>Math.random()</code>, or <code>setTimeout</code> inside tested code as a smell.</strong> Not always a bug — sometimes you genuinely need the current time. But every one of those calls is a hidden input your test does not control, and hidden inputs are where flakiness lives. Inject them or freeze them.
<strong>When a test is flaky, log the environment, not the test.</strong> I wasted an hour re-reading the test. The answer was in the runner: the start time of the run. Flaky tests are usually deterministic functions of something you are not looking at — the clock, the order tests run in, leftover state from a previous test. Log the context first, read the test second.
The test has been green on CI for three weeks now, across dozens of runs. Not because I made it more robust. Because I made it deterministic, and deterministic things do not flake.</p>
]]></content:encoded></item><item><title><![CDATA[SQL Joins Finally Clicked — the 4 Diagrams That Did It]]></title><description><![CDATA[I failed SQL joins three times. I could repeat the definitions word for word, and still stare at a query like it was written in another language. Then a…]]></description><link>https://aheadkit.hashnode.dev/sql-joins-finally-explained-with-pictures-in-your-head</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/sql-joins-finally-explained-with-pictures-in-your-head</guid><category><![CDATA[SQL]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[database]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Fri, 06 Nov 2026 06:28:07 GMT</pubDate><content:encoded><![CDATA[<p>I failed SQL joins three times. I could repeat the definitions word for word, and still stare at a query like it was written in another language. Then a friend drew four circles on a napkin, and it clicked in about ten minutes. She showed me which rows survive, with real data, instead of reciting definitions. Here is that napkin, rebuilt with a small example you can follow.</p>
<h2>The example</h2>
<p>Two tables. Customers, and orders.</p>
<p>Customers:</p>
<p>| id | name |
| 1 | Aisha |
| 2 | Ben |
| 3 | Cara |</p>
<p>Orders:</p>
<p>| order_id | customer_id | item |
| 101 | 1 | Laptop |
| 102 | 1 | Mouse |
| 103 | 2 | Keyboard |</p>
<p>Aisha ordered twice. Ben ordered once. Cara ordered nothing. Every join below starts from these two tables. The only question is: which rows survive?</p>
<h2>1. INNER JOIN: only the matches</h2>
<pre><code>SELECT name, item FROM customers
INNER JOIN orders ON customers.id = orders.customer_id;
</code></pre>
<p>Result:</p>
<p>| name | item |
| Aisha | Laptop |
| Aisha | Mouse |
| Ben | Keyboard |</p>
<p>Cara is gone. She has no order, so she has no match, so she does not appear. This is the join most people want most of the time, and it is also the one that quietly drops people.</p>
<p>Picture two circles overlapping. INNER JOIN is only the overlap. Use it when you only care about customers who actually ordered. Do not use it for a report called "all customers", because it will silently leave Cara out.</p>
<h2>2. LEFT JOIN: everyone on the left, matches where they exist</h2>
<pre><code>SELECT name, item FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id;
</code></pre>
<p>Result:</p>
<p>| name | item |
| Aisha | Laptop |
| Aisha | Mouse |
| Ben | Keyboard |
| Cara | (empty) |</p>
<p>Cara is back, with an empty space where the item would be. LEFT JOIN keeps every row from the left table, customers, and adds the matching order where one exists. Where there is no match, you get an empty value instead of losing the row.</p>
<p>This is the join you want when the question is "show me all customers, and their orders if they have any". It is also how you find customers who never ordered. Add <code>WHERE orders.order_id IS NULL</code> and you get only Cara.</p>
<h2>3. RIGHT JOIN: the mirror image</h2>
<pre><code>SELECT name, item FROM customers
RIGHT JOIN orders ON customers.id = orders.customer_id;
</code></pre>
<p>Result:</p>
<p>| name | item |
| Aisha | Laptop |
| Aisha | Mouse |
| Ben | Keyboard |</p>
<p>Same result as INNER JOIN here, because every order has a matching customer. RIGHT JOIN keeps every row from the right table, orders, and adds the customer where one exists. In practice I almost never write it. If I want the orders side kept whole, I swap the tables and write a LEFT JOIN instead. Learn what it means so you can read it, then write LEFT JOINs in your own code.</p>
<h2>4. FULL JOIN: everyone from both sides</h2>
<pre><code>SELECT name, item FROM customers
FULL JOIN orders ON customers.id = orders.customer_id;
</code></pre>
<p>Result:</p>
<p>| name | item |
| Aisha | Laptop |
| Aisha | Mouse |
| Ben | Keyboard |
| Cara | (empty) |</p>
<p>Again the same as LEFT JOIN here, because there are no orphan orders. FULL JOIN keeps every row from both tables. A customer with no order shows up with an empty item. An order with no customer would show up with an empty name. You will use it less than the others, but when two tables have messy data, it shows you the whole mess at once, including rows that match nothing.</p>
<h2>The one mistake that still catches people</h2>
<p>Here it is. People write a LEFT JOIN, then add a filter on the right table in the WHERE clause, and wonder why Cara disappeared again.</p>
<pre><code>SELECT name, item FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id
WHERE item = 'Laptop';
</code></pre>
<p>Cara has no item. The WHERE clause checks her empty item against 'Laptop', it fails, and she is gone. Your LEFT JOIN just turned into an INNER JOIN, and nothing warned you.</p>
<p>If you need to filter the right table, put the filter in the ON clause, not the WHERE clause.</p>
<pre><code>SELECT name, item FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id AND item = 'Laptop';
</code></pre>
<p>Now Cara stays, with an empty item, and Aisha shows only her laptop. The join keeps its promise.</p>
<p>I check for this mistake in every query I write now. It makes reports quietly wrong instead of loudly wrong. Loudly wrong is fine. You notice it. Quietly wrong ships to a manager.</p>
<h2>How to remember them</h2>
<p>Do not memorise the definitions. Memorise the question each join answers. INNER JOIN: who has both? LEFT JOIN: who is on the left, and what did they do? RIGHT JOIN: the same, from the other side. FULL JOIN: who is anywhere at all?</p>
<p>Run the example above on your own machine, with these exact tables. Change one row. Delete Ben's order. Add a customer with no orders. Watch the results change. Ten minutes with real rows beats an hour of reading definitions.</p>
]]></content:encoded></item><item><title><![CDATA[Your LinkedIn Headline Is Invisible to Recruiters — Fix It in 10 Minutes]]></title><description><![CDATA[I spent a month wondering why nobody messaged me on LinkedIn. My profile was complete, my experience section was solid, and I had applied to plenty of…]]></description><link>https://aheadkit.hashnode.dev/your-linkedin-headline-is-invisible-to-recruiters-fix-it-in-10-minutes</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/your-linkedin-headline-is-invisible-to-recruiters-fix-it-in-10-minutes</guid><category><![CDATA[Career]]></category><category><![CDATA[LinkedIn]]></category><category><![CDATA[jobs]]></category><category><![CDATA[careeradvice]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Fri, 06 Nov 2026 06:16:06 GMT</pubDate><content:encoded><![CDATA[<p>I spent a month wondering why nobody messaged me on LinkedIn. My profile was complete, my experience section was solid, and I had applied to plenty of jobs. Then a recruiter friend showed me what she actually sees.</p>
<p>She typed "junior data analyst" into search. A wall of names came back. Mine was there, somewhere in the middle, and my headline read "Data Analyst at XYZ Corp". She scrolled straight past it. Not because my experience was weak. Because my headline told her nothing she did not already know from the job title filter.</p>
<p>That is the failure. Most headlines are just a job title repeated back. Recruiters search by skills, tools, and problems solved. A headline that only says your current title gives search nothing to grab onto, and gives a human reader no reason to click.</p>
<p>Here is the fix, in ten minutes. Set a timer. It really does take ten minutes, and most of that is deciding which words to keep.</p>
<h2>Minute 0 to 2: Pick your three search terms</h2>
<p>Open five job posts you actually want. Not dream jobs. Jobs you would take tomorrow. Write down the words that repeat across them.</p>
<p>For a data analyst, those words are usually SQL, dashboards, and Excel or Python. For a designer, Figma, user research, and mobile apps. For a junior developer, React, APIs, and testing.</p>
<p>Pick three. These are the words a recruiter types. If your headline does not contain them, you are invisible in that search, no matter how good your experience section is.</p>
<h2>Minute 2 to 6: Rewrite it with this shape</h2>
<p>Role plus what you do plus proof. Keep it under 200 characters so it does not cut off on mobile.</p>
<p>Here are three before and after versions, from real profiles I rewrote for friends.</p>
<p><strong>1. The junior data analyst</strong></p>
<p>Before: "Data Analyst at XYZ Corp"
After: "Data Analyst | SQL + Python dashboards that cut weekly reporting from 6 hours to 40 minutes"</p>
<p>The second version has the search terms, the tool, and a number. A recruiter can picture the work in one line. The number is the part that makes someone click. It does not need to be huge. Forty minutes saved a week is real work.</p>
<p><strong>2. The fresh graduate looking for a marketing role</strong></p>
<p>Before: "Marketing Graduate | Seeking Opportunities"
After: "Marketing Graduate | Ran a student society Instagram from 400 to 3,200 followers in 5 months"</p>
<p>"Seeking opportunities" is the worst phrase on LinkedIn. Every graduate is seeking opportunities. The rewrite replaces it with the one thing this person actually did, with numbers. Recruiters hire evidence, not availability.</p>
<p><strong>3. The career changer moving into UX</strong></p>
<p>Before: "Aspiring UX Designer"
After: "UX Designer | Ex-teacher turning classroom research into clear, tested app flows"</p>
<p>"Aspiring" tells a recruiter you have not done the work yet. The rewrite owns the past career instead of apologising for it. Teaching is user research with harder users. Say that.</p>
<h2>Minute 6 to 8: Delete the filler</h2>
<p>Read your new headline out loud. Cut any word that does not earn its place. Cut "passionate", "results-driven", "detail-oriented", and "hardworking". Nobody has ever been hired because their headline said "hardworking". Those words take up space that a skill or a number could use.</p>
<p>If you have a certification that employers actually search for, keep it. "AWS Certified" earns its place. "Lifelong learner" does not.</p>
<h2>Minute 8 to 10: Check it the way a recruiter does</h2>
<p>Search LinkedIn for your own three terms. Look at the first ten profiles. Does yours now read like one of the useful ones, or does it still blend in?</p>
<p>Then check it on your phone. LinkedIn cuts headlines off after about 120 characters on a small screen. Put the role and the strongest skill first, the proof second. If the number gets cut off, move it earlier.</p>
<h2>What changed for me</h2>
<p>I rewrote mine on a Tuesday night. By Friday I had three profile views from recruiters, and one message asking if I was open to a contract role. That had never happened before. My experience did not change in those three days. Only the headline did.</p>
<p>I am not saying a headline gets you a job. It gets you seen, and seen is the step before everything else. A recruiter cannot shortlist a profile she scrolls past.</p>
<p>Ten minutes. Three search terms. One number. Do it tonight, before you send the next application, because the next application lands better when the person who opens your profile can see in one line why you are worth a reply.</p>
]]></content:encoded></item><item><title><![CDATA[I Rebuilt My Developer Portfolio. The Section I Deleted Mattered Most]]></title><description><![CDATA[My old portfolio had everything: a skills section with animated bars, an about page with my life story, nine projects, a services list, a testimonials…]]></description><link>https://aheadkit.hashnode.dev/i-rebuilt-my-developer-portfolio-the-section-i-deleted-mattered-most</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/i-rebuilt-my-developer-portfolio-the-section-i-deleted-mattered-most</guid><category><![CDATA[Career]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[portfolio]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Thu, 05 Nov 2026 11:27:09 GMT</pubDate><content:encoded><![CDATA[<p>My old portfolio had everything: a skills section with animated bars, an about page with my life story, nine projects, a services list, a testimonials section borrowed from a tutorial, and a contact form that sent email to an account I rarely opened.</p>
<p>The rebuild kept three projects and deleted most of the rest. The replies came after the deletions, not the additions. Here's what I learned about how portfolios actually get read.</p>
<h2>How hiring eyes actually move</h2>
<p>Nobody reads a junior developer's portfolio like a book. They scan it like a menu in a restaurant they're not sure about. Roughly: land on the page, look for projects, click the first live demo, spend under a minute inside it, check whether the code is viewable, then decide whether your name goes on the maybe pile.</p>
<p>That scan order tells you where the weight sits. The demo and the README carry the whole verdict. Everything else is decoration, and some of it is worse than decoration because it eats the seconds the scan would have spent on your projects.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/282e0924-be03-49be-895e-846e75b0c886.png" alt="" style="display:block;margin:0 auto" />

<p><em>Caption: A GitHub profile — for most junior roles, this page gets more scrutiny than the portfolio site itself, and the pinned projects do the talking.</em></p>
<h2>What to cut, starting with the section I deleted</h2>
<p><strong>Skill bars.</strong> The section I deleted entirely. "JavaScript: 85%" raises one question — 85% of what? — and answers none. Worse, it invites the reader to grade you against a scale you invented. A project that uses JavaScript proves more about your JavaScript than any percentage. Cut the bars, the pie charts, and the star ratings. If a skill matters for the role, it should be visible inside a project.</p>
<p><strong>The generic intro.</strong> "I am a passionate developer who loves building scalable solutions" could sit on ten thousand portfolios unchanged, which makes it invisible. Replace it with one specific line: what you build, and one true thing about it. "I build small web tools; my last one converts bank CSVs into clean spreadsheets" gives a stranger something to ask you about.</p>
<p><strong>Tutorial clones with no twist.</strong> A to-do app, a weather app, and a Netflix-looking homepage tell a reviewer you've completed tutorials, which they already assumed. A clone earns its place only if you changed it in a way that was your idea: the weather app that texts you when rain would hit your commute route is a project; the weather app from the video is homework.</p>
<p><strong>Anything empty or borrowed.</strong> A testimonials section with quotes from nobody real, a blog tab with one post from eight months ago, a services list for work you've never been hired to do. Empty sections don't look ambitious. They look abandoned.</p>
<h2>The 3-project structure that replaced it</h2>
<p>Three projects, presented the same way each time:</p>
<ol>
<li><strong>The most complete one first.</strong> Not the newest — the one with a live demo, a real problem, and code you'd defend in an interview.</li>
<li><strong>Each project gets four things:</strong> one line on the problem, a live link, a screenshot or short recording, and a README that names what you personally did. "Built the checkout flow and the retry logic for failed payments" beats "we built an amazing app" every time. Reviewers know teams exist; they want your slice of it.</li>
<li><strong>One project should be slightly odd.</strong> The boring clone proves you can follow. The odd one (the tool you built for your uncle's shop, the bot that tracks your bus route) proves you can start. Interviewers ask about the odd one.</li>
</ol>
<h2>Before and after, as a list</h2>
<p><strong>Before:</strong> Hero with buzzword intro · Skills with bars (14 skills) · About page · 9 projects, no live demos on 6 · Testimonials · Blog (1 post) · Services · Contact form</p>
<p><strong>After:</strong> One-line intro naming what I build · 3 projects, each with live demo + README · Short about paragraph at the bottom · Email address, plain text, on the first screen</p>
<p>The page got shorter and the replies got longer. That's the trade.</p>
<h2>The uncomfortable summary</h2>
<p>Deleting the skills section worked because it was the loudest claim on the page with the least evidence behind it. Your portfolio doesn't need more sections; it needs every section to survive one question: "can I check that?" Live demo, readable code, a README in your own words. If a section can't be checked, it reads as filler, and filler makes the real work look smaller. Cut first, polish second.</p>
]]></content:encoded></item><item><title><![CDATA[5 Excel Formulas That Give You Back an Hour a Week]]></title><description><![CDATA[Nobody's job description says "spend Friday afternoon copying values between tabs." Yet that's where the hour goes. These five formulas each kill one…]]></description><link>https://aheadkit.hashnode.dev/5-excel-formulas-that-give-you-back-an-hour-a-week</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/5-excel-formulas-that-give-you-back-an-hour-a-week</guid><category><![CDATA[excel]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[spreadsheets]]></category><category><![CDATA[Career]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Thu, 05 Nov 2026 11:13:24 GMT</pubDate><content:encoded><![CDATA[<p>Nobody's job description says "spend Friday afternoon copying values between tabs." Yet that's where the hour goes. These five formulas each kill one specific manual chore. Each one below names the chore first, because a formula you can't match to a job you hate is trivia.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/17c8da01-65db-4bdb-aaff-38195ae39d00.png" alt="" style="display:block;margin:0 auto" />

<p><em>Caption: Microsoft Excel — the five formulas below cover most of the copy-paste work in a typical reporting sheet.</em></p>
<h2>1. XLOOKUP — replaces hunting through a second tab by eye</h2>
<p>The chore: you have orders in one tab and a price list in another, and you're scrolling back and forth typing prices in by hand. Or worse, using VLOOKUP and praying nobody inserts a column.</p>
<pre><code>=XLOOKUP(A2, Products!$A:$A, Products!$B:$B, "not listed")
</code></pre>
<p>Read it left to right: take the product code in A2, find it in the Products tab's column A, bring back the price from column B, and if it's missing, write "not listed" instead of an error. That fourth argument is the whole ballgame — the lookup handles its own failure.</p>
<p><em>Google Sheets:</em> XLOOKUP exists there too, same shape. Older sheets files may still use VLOOKUP; XLOOKUP reads left to right in plain order and doesn't break when columns move.</p>
<h2>2. SUMIFS — replaces filter, select, read the status bar, unfilter, repeat</h2>
<p>The chore: "How much did the Dhaka office spend on travel in March?" You filter three times, squint at the total in the status bar, write it on a sticky note, then do it again for every office.</p>
<pre><code>=SUMIFS(Spend!$C:$C, Spend!$A:$A, "Dhaka", Spend!$B:$B, "Travel")
</code></pre>
<p>Translation: add up column C, but only rows where column A says Dhaka and column B says Travel. Change the two words and the next answer takes four seconds. Build a small grid of offices down one side and categories across the top, point the formula at the grid cells instead of typed words, and you've built the report you used to assemble monthly.</p>
<p><em>Google Sheets:</em> SUMIFS works identically.</p>
<h2>3. TEXTSPLIT — replaces the Text to Columns wizard, every single import</h2>
<p>The chore: a system exports names as "Rahim Uddin, Dhaka, 0171..." in one cell and you need three cells. You select, open the wizard, click through, and repeat next week when the export lands again.</p>
<pre><code>=TEXTSPLIT(A2, ", ")
</code></pre>
<p>One formula splits the cell at every comma-space and spills the parts across the row. Next week's export? Paste it under the old one, copy the formula down, done. The chore is now ten seconds old instead of ten minutes.</p>
<p><em>Google Sheets:</em> no TEXTSPLIT; use <code>=SPLIT(A2, ", ")</code>, which does the same job with a slightly older name. Going the other way, TEXTJOIN (available in both) glues cells back together with a separator of your choice.</p>
<h2>4. UNIQUE — replaces copy tab, Remove Duplicates, hope you pasted the right range</h2>
<p>The chore: you need the clean list of customers, departments, or product codes with each appearing once, for a dropdown or a summary. The manual route copies data to a scratch tab and runs Remove Duplicates, and it's stale the moment the source changes.</p>
<pre><code>=UNIQUE(A2:A500)
</code></pre>
<p>The list rebuilds itself whenever the source data changes. Pair it with SORT and it even tidies up after itself: <code>=SORT(UNIQUE(A2:A500))</code>. Feed a dropdown from it and the dropdown stays current forever, which is more than can be said for any list you've ever pasted.</p>
<p><em>Google Sheets:</em> UNIQUE and SORT both exist with the same names.</p>
<h2>5. IFERROR around a lookup — replaces scanning for #N/A by eye</h2>
<p>The chore: a column of lookups with a handful of angry #N/A errors scattered through it, and you're scrolling to find each one before sending the file to someone senior.</p>
<pre><code>=IFERROR(XLOOKUP(A2, Products!$A:$A, Products!$B:$B), "check code")
</code></pre>
<p>IFERROR runs the formula inside it and, if that formula throws an error, shows your message instead. The file now reads cleanly, and the problems announce themselves in words ("check code") instead of error codes. One warning from experience: don't wrap formulas in IFERROR while you're still building them. It hides real mistakes too, and you'll debug a silent blank cell for twenty minutes. Add it last, as the coat of paint.</p>
<p><em>Google Sheets:</em> IFERROR works the same way.</p>
<h2>The hour, accounted for</h2>
<p>Ten minutes of manual lookups, fifteen of filter-and-squint totals, ten on splitting imported text, ten on duplicate lists, ten on error hunting. That's most people's spreadsheet Friday. None of these five formulas is advanced; each is one line. The trick was never learning them, it was noticing that the chore you repeat weekly is a formula wearing a disguise.</p>
]]></content:encoded></item><item><title><![CDATA[ChatGPT vs Claude vs Gemini for Coding: I Ran the Same 3 Tasks Through All of Them]]></title><description><![CDATA[Same three prompts, pasted into all three tools, on their free plans. No stopwatch screenshots, no cherry-picked tenth attempt. What follows is the first…]]></description><link>https://aheadkit.hashnode.dev/chatgpt-vs-claude-vs-gemini-for-coding-i-ran-the-same-3-tasks-through-all-of-them</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/chatgpt-vs-claude-vs-gemini-for-coding-i-ran-the-same-3-tasks-through-all-of-them</guid><category><![CDATA[AI]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[chatgpt]]></category><category><![CDATA[Productivity]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Thu, 05 Nov 2026 11:03:40 GMT</pubDate><content:encoded><![CDATA[<p>Same three prompts, pasted into all three tools, on their free plans. No stopwatch screenshots, no cherry-picked tenth attempt. What follows is the first decent answer each one gave, judged against the limits each company actually publishes, because for coding help the limits decide more than the marketing does.</p>
<p>Quick spec check, because these numbers shape every verdict below. ChatGPT's free plan runs its smaller GPT-5.6 Luna model with a 27K-token context window and 3 file uploads a day. Claude's free plan gives you its Sonnet model with a 200K-token context window and Artifacts, metered in rolling five-hour sessions (roughly 15–40 messages per session depending on length). Gemini's free plan runs its fast Flash models with a 32K-token context window, Canvas included, and the big 1M window is paid-only. Those three rows explain most of what happened.</p>
<h2>Task 1: Explain an error message</h2>
<p>The prompt: a real TypeError stack trace from a small Node script, pasted with the offending function.</p>
<p>All three found the actual cause. Nobody hallucinated a fix for a different bug, which was not guaranteed a couple of years ago. The difference was in the shape of the explanation.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/0afd60f4-7f24-4787-b3d4-d3941a845724.png" alt="" style="display:block;margin:0 auto" />

<p><em>Caption: ChatGPT — fastest route from error message to a plain-English cause.</em></p>
<p>ChatGPT answered shortest: the cause, the line, the corrected line. If you're mid-debugging and annoyed, that's the tone you want. Claude explained the same cause but added why the value became undefined two calls earlier, which is the part that stops the bug coming back next week. Gemini matched ChatGPT on speed and added a short list of related mistakes, decent, slightly more than I asked for.</p>
<p><strong>Verdict:</strong> a tie you split by mood. Fast fix, ChatGPT. Understanding that prevents a repeat, Claude.</p>
<h2>Task 2: Write a small function with edge cases</h2>
<p>The prompt: a function that applies a percentage discount to a list of prices, handling an empty list, negative prices, and non-numeric junk in the input.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/f918d771-a168-4cc8-acef-e8ce75bfc25c.png" alt="" style="display:block;margin:0 auto" />

<p><em>Caption: Claude — Artifacts let you see and run small front-end code without leaving the chat.</em></p>
<p>All three wrote working code and handled the empty list. Claude and ChatGPT both rejected negative prices explicitly; Gemini clamped them to zero, which is a defensible choice it didn't flag as a choice. Small thing, but silent decisions in code are how bugs get tenure.</p>
<p>The real split here is the workspace. When the task is a small self-contained widget (a converter, a form validator), Claude's Artifacts and Gemini's Canvas both render it next to the chat so you can poke it immediately. ChatGPT has Canvas too, but on free the tighter upload and session limits make longer back-and-forth sessions bump into walls sooner.</p>
<p><strong>Verdict:</strong> Claude, by a nose, because the edge-case reasoning was spelled out instead of buried in the code.</p>
<h2>Task 3: Refactor a messy file</h2>
<p>The prompt: one ugly 400-line utility file, pasted whole, asked to split it into named functions without changing behaviour.</p>
<p>This is where the spec sheet stopped being trivia. The file ran to roughly 30K tokens with the prompt attached. That is over ChatGPT free's 27K window and right at the edge of Gemini free's 32K, so both quietly worked from a truncated file and produced refactors that dropped a helper function near the end. Quietly is the problem. Neither threw up a big warning; the output just looked complete.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/e77a346f-7a5a-4390-a9f4-077aefa20acf.png" alt="" style="display:block;margin:0 auto" />

<p><em>Caption: Gemini — Canvas and Deep Research are free, but the free context window is the tight ceiling for whole-file work.</em></p>
<p>Claude's 200K window swallowed the file without blinking, and the refactor kept every function. This isn't a brains contest; it's a bucket-size contest. On paid plans the picture changes (Gemini Pro's 1M window is the biggest bucket here, ChatGPT Plus raises its limits too), but on free, whole-file work has one home.</p>
<p><strong>Verdict:</strong> Claude, and it isn't close on the free tier.</p>
<h2>Pick by situation</h2>
<table>
<thead>
<tr>
<th>Your situation</th>
<th>Pick</th>
<th>Why</th>
</tr>
</thead>
<tbody><tr>
<td>Quick error explained, back to work</td>
<td>ChatGPT</td>
<td>Shortest path, unlimited everyday chat on free</td>
</tr>
<tr>
<td>Learning why the bug happened</td>
<td>Claude</td>
<td>Explains the cause chain, not just the fix</td>
</tr>
<tr>
<td>Paste a whole file or long log</td>
<td>Claude (free)</td>
<td>200K context vs 27K / 32K on the other free plans</td>
</tr>
<tr>
<td>Small widget you want to see running</td>
<td>Claude or Gemini</td>
<td>Artifacts / Canvas render it beside the chat</td>
</tr>
<tr>
<td>Long research-then-code session</td>
<td>Gemini</td>
<td>Deep Research is free; mind the weekly usage cap</td>
</tr>
<tr>
<td>High-volume daily prompting</td>
<td>ChatGPT</td>
<td>Claude free's ~5-hour session meter runs out first</td>
</tr>
</tbody></table>
<p>My honest summary after the three tasks: for day-to-day coding questions, the gap between these tools is smaller than the gap between their free-plan limits. Pick the one whose ceiling you won't hit. For most people doing most coding, that means ChatGPT for volume, Claude for anything long or worth understanding properly, and Gemini when the work starts with research. Then stop switching tools and go finish the project; that part, sadly, none of them do for you.</p>
]]></content:encoded></item><item><title><![CDATA[JavaScript Promises Without the Headache]]></title><description><![CDATA[You order food at a busy counter. The cashier doesn't make you stand there holding up the line while the kitchen cooks. You get a token with a number on…]]></description><link>https://aheadkit.hashnode.dev/javascript-promises-without-the-headache</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/javascript-promises-without-the-headache</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Thu, 05 Nov 2026 10:58:59 GMT</pubDate><content:encoded><![CDATA[<p>You order food at a busy counter. The cashier doesn't make you stand there holding up the line while the kitchen cooks. You get a token with a number on it. You sit down. One of two things happens later: your number is called and you collect your food, or someone comes out and tells you the kitchen has run out of what you ordered.</p>
<p>That token is a promise. Everything else in this article is just the token system with code attached.</p>
<h2>The token, in code</h2>
<p>A promise is an object that stands in for a result that doesn't exist yet. It starts <strong>pending</strong> (you're holding the token, waiting). It ends in exactly one of two states:</p>
<ul>
<li><strong>Fulfilled</strong> — your number is called. You collect the food. This is <code>.then()</code>.</li>
<li><strong>Rejected</strong> — the kitchen failed. Someone tells you why. This is <code>.catch()</code>.</li>
</ul>
<pre><code class="language-js">const order = getFoodToken(); // returns a Promise

order
  .then(meal =&gt; eat(meal))                          // token called: collect your food
  .catch(problem =&gt; orderSomethingElse(problem));   // kitchen ran out: do this instead
</code></pre>
<p><code>fetch()</code> hands you a token. The menu hasn't downloaded yet, but you already have the object, and you can attach instructions for both endings right now:</p>
<pre><code class="language-js">fetch("https://example.com/menu")
  .then(response =&gt; response.json())
  .then(menu =&gt; showMenu(menu))
  .catch(err =&gt; showError("Menu failed to load"));
</code></pre>
<p>Read it as a sentence: fetch the menu, then read it, then show it. If anything in that chain fails, the catch at the bottom handles it. One catch covers the whole chain, the same way one announcement covers everyone holding a token from that kitchen.</p>
<h2>async/await is the same kitchen, better seating</h2>
<p><code>async</code> and <code>await</code> don't replace promises. They let you write the token flow as if you had a waiter:</p>
<pre><code class="language-js">async function loadMenu() {
  try {
    const response = await fetch("https://example.com/menu");
    const menu = await response.json();
    showMenu(menu);
  } catch (err) {
    showError("Menu failed to load");
  }
}
</code></pre>
<p><code>await</code> means: pause here until this token is called, then hand me the food. The <code>try/catch</code> is your <code>.then()</code> and <code>.catch()</code> wearing different clothes. Underneath, it is the same promise. Use whichever reads better in the code you're writing; most people find await easier once a function has three or four steps.</p>
<h2>The 3 mistakes that cause most promise bugs</h2>
<p><strong>1. Forgetting to return inside <code>.then()</code>.</strong> This one bites everyone.</p>
<pre><code class="language-js">// broken: the next .then receives undefined
fetch(url)
  .then(response =&gt; { response.json(); })
  .then(data =&gt; console.log(data)); // undefined

// fixed: return the next token
fetch(url)
  .then(response =&gt; response.json())
  .then(data =&gt; console.log(data));
</code></pre>
<p>Why: each <code>.then()</code> passes its return value to the next one. <code>response.json()</code> itself returns a promise, and if you don't return it, the chain doesn't wait for it. It's like collecting your token number but never handing it to the person at the next counter. The food exists; nobody brings it to you.</p>
<p><strong>2. Mixing callbacks into a promise chain.</strong> You'll meet older functions that want a callback instead of returning a promise. Don't thread them through a chain. Wrap them once, at the edge:</p>
<pre><code class="language-js">const wait = ms =&gt; new Promise(resolve =&gt; setTimeout(resolve, ms));

await wait(1000); // a clean token, no callback nesting
</code></pre>
<p>One wrapper, written once, and the rest of your code stays flat and readable.</p>
<p><strong>3. Skipping <code>.catch()</code>.</strong> A rejected promise with no catch doesn't stop your program politely. It produces an unhandled rejection, which usually means the user stares at a spinner forever and you get a confusing console warning hours later. In token terms: the kitchen called out that your order failed, and nobody at your table was listening. Every chain gets a catch, or every async function gets a try/catch. No exceptions, even for code you're "sure" won't fail. Kitchens run out of things.</p>
<h2>The 10-line cheat summary</h2>
<ol>
<li>A promise is a token for a result that isn't ready yet.</li>
<li>Pending means you're still holding the token.</li>
<li>Fulfilled means the food arrived: <code>.then()</code> runs.</li>
<li>Rejected means the kitchen failed: <code>.catch()</code> runs.</li>
<li><code>fetch()</code> returns a promise, not data.</li>
<li>Return values inside <code>.then()</code>, or the next step gets <code>undefined</code>.</li>
<li>Returning a promise inside <code>.then()</code> makes the chain wait for it.</li>
<li><code>await</code> pauses an async function until the token is called.</li>
<li><code>try/catch</code> around awaits is <code>.then()</code>/<code>.catch()</code> in a calmer format.</li>
<li>No catch means silent failure. Always attach one.</li>
</ol>
<p>Keep the token picture in your head for a week and promises stop being a topic you revise and start being a thing you just read. The syntax was never the hard part. The waiting was.</p>
]]></content:encoded></item><item><title><![CDATA[Your Resume Isn't Bad. The Robot Reading It Is — a 15-Minute ATS Fix]]></title><description><![CDATA[You spent an evening on your resume. A recruiter never saw it. Somewhere between you clicking "submit" and a human opening the file, a piece of software…]]></description><link>https://aheadkit.hashnode.dev/your-resume-isn-t-bad-the-robot-reading-it-is-a-15-minute-ats-fix</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/your-resume-isn-t-bad-the-robot-reading-it-is-a-15-minute-ats-fix</guid><category><![CDATA[resume]]></category><category><![CDATA[Career]]></category><category><![CDATA[jobs]]></category><category><![CDATA[careeradvice]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Thu, 05 Nov 2026 10:52:17 GMT</pubDate><content:encoded><![CDATA[<p>You spent an evening on your resume. A recruiter never saw it. Somewhere between you clicking "submit" and a human opening the file, a piece of software read it first, pulled it apart looking for specific words, and ranked it against everyone else. Your resume did not fail an interview. It failed a parsing job.</p>
<p>That software is an ATS, an applicant tracking system, and the good news is that it is not judging your career. It is doing three much dumber jobs, and all three can be fixed in about 15 minutes.</p>
<h2>What an ATS actually does</h2>
<p>Strip away the marketing and an ATS does this:</p>
<ol>
<li><strong>Parse.</strong> It converts your file into plain text and tries to sort it into boxes: name, contact details, job titles, employers, dates, education, skills. If it cannot find the boxes, parts of your history simply vanish.</li>
<li><strong>Match.</strong> It compares the text it extracted against the job description, looking for the skills, tools and titles the employer asked for.</li>
<li><strong>Rank.</strong> It scores and sorts the pile, so a recruiter works from the top down. Many resumes are never opened by a person at all.</li>
</ol>
<p>That is it. No taste, no judgement, no sense that your startup job title meant three roles at once. If the parser drops your experience because it was hiding inside a text box, the ranker cannot reward what it never saw.</p>
<h2>The 6 formatting mistakes that break parsing</h2>
<p><strong>1. Tables and multi-column layouts.</strong> The classic two-column resume with a tidy sidebar is the single biggest offender. Many parsers read straight across the page, so your sidebar skills get stitched into the middle of your job descriptions and the whole thing turns into soup.</p>
<p><strong>2. Headers and footers.</strong> Plenty of systems skip headers and footers entirely. If your name, email and phone number live in the header, the system may process your entire application without your contact details.</p>
<p><strong>3. Icons, skill bars and graphics.</strong> That neat row of icons and the little bars showing "Excel: 4 out of 5" contain no readable text. The parser sees blank space where your skills were supposed to be.</p>
<p><strong>4. Decorative fonts.</strong> A display font can break character extraction, turning clean text into garbled output. Stick to standard fonts the way you would stick to a standard file name.</p>
<p><strong>5. The wrong file type.</strong> Unless the posting says otherwise, send a .docx or a text-based PDF. A PDF exported from a design tool as one big image is, to the parser, a photograph of a resume. It reads almost nothing from it.</p>
<p><strong>6. Keyword-stuffed walls.</strong> Swinging to the other extreme does not work either. A hidden block of repeated keywords in white text or a dense skills paragraph copied wholesale from the posting reads as spam to modern systems, and a human who spots it will bin the application on principle. Keywords belong inside real bullet points about work you actually did.</p>
<h2>The 15-minute fix</h2>
<p>You need your resume file and one real job description you want to apply for. Timer optional but encouraged.</p>
<p><strong>Minutes 0–3: Flatten the layout.</strong> Switch to a single column, newest job first. Move your name, email, phone and location out of the header and into the top of the document body. Delete skill bars, icons and text boxes.</p>
<p><strong>Minutes 3–8: Fix the skeleton.</strong> Use plain headings a parser recognises: Summary (optional), Experience, Skills, Education. Under Experience, every role gets the same pattern: job title, company, dates on their own line. Use standard bullet points, the round kind, not custom glyphs.</p>
<p><strong>Minutes 8–12: Borrow the job's own words, honestly.</strong> Read the job description and list its required skills and tools. Go through that list and, wherever you genuinely have the skill, make sure your resume uses the employer's exact term for it. If they wrote "data analysis" and you wrote "number crunching", theirs wins. Do not add anything you cannot talk about in an interview. This step is translation, not fiction.</p>
<p><strong>Minutes 12–15: Save and test.</strong> Save as .docx (or a text-based PDF if the posting asks for PDF) with a plain file name like Firstname-Lastname-Resume.docx. Then run the free checks below and fix whatever they flag about unreadable sections before you send anything.</p>
<h2>Check your own resume, free</h2>
<p>Two tools will show you roughly what the robot sees:</p>
<ul>
<li><strong>Jobscan</strong> lets you paste your resume against a job description and gives you a match rate with the missing keywords listed. The free account includes 5 scans a month, which is plenty if you save them for jobs you seriously want.</li>
</ul>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/50e54030-3230-4620-9ba9-b6369c2a2517.png" alt="" style="display:block;margin:0 auto" />

<p><em>Jobscan: your resume scored against a real job description, missing keywords shown.</em></p>
<ul>
<li><strong>Resume Worded</strong> gives you a free score out of 100 with a partial report, and re-uploads are free, so you can fix and re-check. The full line-by-line report is a paid Pro feature, but the free score plus its headline feedback already catches most formatting and weak-bullet problems.</li>
</ul>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/fe110cfd-17c8-45c3-978a-04275eabe89a.png" alt="" style="display:block;margin:0 auto" />

<p><em>Resume Worded: a free score out of 100, with recruiter-style feedback on your bullets.</em></p>
<p>Neither tool is the employer's actual system, so treat the scores as a smoke test, not a verdict. If both can read your resume cleanly, the real thing almost certainly can too.</p>
<h2>One last thing: the 30 words a recruiter actually reads</h2>
<p>Pass the robot and you meet the human, who is also fast. Recruiters skim a resume in seconds, and the honest version of the famous statistic is that only a small slice of the page gets real attention on the first pass: your last job title, your last employer, your dates, and the first bullet or two under that role. Call it roughly 30 words deciding whether the rest gets read.</p>
<p>So put your strongest, most measurable result in the first bullet of your most recent job. "Answered support tickets" survives parsing and dies on the skim. "Cut average ticket response time from 9 hours to 2" survives both.</p>
<p>And none of this means your resume has to be ugly. Clean, well-spaced, single-column resumes look professional to humans and readable to software at the same time. The robot is not asking you to be boring. It is asking you to be legible. Fifteen minutes, and you are.</p>
]]></content:encoded></item><item><title><![CDATA[Python or JavaScript First? Decide by the Job You Want — A 2-Question Test]]></title><description><![CDATA["Python or JavaScript?" is the question almost every beginner asks first, and the internet answers it with salary tables and arguments about syntax. Both…]]></description><link>https://aheadkit.hashnode.dev/python-or-javascript-first-decide-by-the-job-you-want-a-2-question-test</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/python-or-javascript-first-decide-by-the-job-you-want-a-2-question-test</guid><category><![CDATA[Python]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Wed, 04 Nov 2026 04:15:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/a499f79e-2b1b-472c-9514-2a01308c872b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>"Python or JavaScript?" is the question almost every beginner asks first, and the internet answers it with salary tables and arguments about syntax. Both are the wrong way to decide. Your first language is not a lifetime commitment. It is the tool you will use to get your first real projects finished and, for many people, a first job. So decide by the work, not by the debate.</p>
<p>Two questions settle it in about ten minutes. Answer them honestly, on paper, and the choice usually makes itself.</p>
<h2>Question 1: What do you want to have built six months from now?</h2>
<p>Picture one finished thing — not a course certificate, an actual project you can show someone.</p>
<ul>
<li>If it lives in a browser — a website, a web app, a page that reacts when someone clicks — that is JavaScript territory. JavaScript is the only programming language web browsers run natively. Python cannot make a web page interactive; no tool changes that.</li>
<li>If it works behind the scenes — a script that renames 500 files, a program that pulls numbers from a spreadsheet and summarises them, a small bot, an experiment with an AI model — that is Python territory. JavaScript can do some of this through Node.js, but the tools, tutorials, and libraries for data and automation overwhelmingly assume Python.</li>
</ul>
<p>This question matters more than "which is easier" because motivation runs out. A beginner who wants to build websites will quit Python in week five, not because Python is bad, but because none of their practice projects look like the thing they actually want. The reverse happens just as often.</p>
<h2>Question 2: Which ten job ads would you actually apply to?</h2>
<p>Open a job board, search for junior or entry-level roles you would genuinely apply for in six to twelve months, and tally the languages in the requirements. Ten ads is enough.</p>
<ul>
<li>Mostly frontend or full-stack web roles? You will likely count JavaScript in eight or nine of them.</li>
<li>Data analyst, QA automation, IT support with scripting, research assistant roles? Python usually dominates that list.</li>
</ul>
<p>This beats global salary rankings for a simple reason: hiring is local and role-specific. A worldwide average salary for "Python developer" tells you nothing about the twenty companies actually hiring juniors near you, or remotely in your timezone. The ads you tally are your real market, describing real first jobs.</p>
<p>When both questions point the same way, stop researching. That agreement is your answer.</p>
<h2>What the numbers say (and why they don't decide it)</h2>
<p>In Stack Overflow's 2025 Developer Survey, 66% of working developers reported using JavaScript and 57.9% reported using Python. Among people learning to code, Python led at 71.8%, with JavaScript at 62.8%. In plain terms: these are the two most travelled roads in programming, and both are busy.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/c23af74d-6241-4d50-8b7c-6ab9f78868a2.png" alt="Stack Overflow Developer Survey chart of most-used programming languages" style="display:block;margin:0 auto" />

<p><em>The Stack Overflow Developer Survey's language chart — JavaScript and Python sit at the top for working developers and learners alike.</em></p>
<p>Read that as reassurance, not as a verdict. Neither choice locks a door. Employers do not reject a good junior because their first language was the "wrong" one of these two.</p>
<h2>If your answers point to Python</h2>
<p>Start at <a href="http://python.org">python.org</a>. Install Python from there, and keep the official tutorial open as your reference while you learn — the same docs every other resource quietly points back to.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/50e2d8d2-c3eb-4d9c-a891-c6bebd91d093.png" alt="The python.org homepage — where to download Python and find the official tutorial" style="display:block;margin:0 auto" />

<p><a href="http://python.org"><em>python.org</em></a> <em>— the download, the official tutorial, and the documentation all live here.</em></p>
<p>A sensible first stretch looks like this:</p>
<ul>
<li>Weeks 1–3: variables, loops, lists, dictionaries, functions — small exercises daily.</li>
<li>Weeks 4–6: work with files. Read a CSV of expenses, clean it, print a summary. Rename and sort a messy downloads folder.</li>
<li>Weeks 7–12: one project that solves a real annoyance in your own life. Boring is good. Finished is better.</li>
</ul>
<p>Notice what is missing: no framework, no database, no cloud anything. Those come after you can write a small program alone, start to finish.</p>
<h2>If your answers point to JavaScript</h2>
<p>JavaScript comes with two companions: HTML (what a page contains) and CSS (how it looks). You do not need to master them first — a week of basics is enough to start — but expect to grow all three together.</p>
<p>The tutorial at <a href="http://javascript.info">javascript.info</a> is the most complete free path through the language itself, from first steps to the awkward corners, with small tasks built in.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/db95899d-72f2-4f55-a44f-e0741e929258.png" alt="The javascript.info homepage showing the JavaScript tutorial contents" style="display:block;margin:0 auto" />

<p><a href="http://javascript.info"><em>javascript.info</em></a> <em>— a full, free JavaScript tutorial that runs from the basics to advanced topics in order.</em></p>
<p>A sensible first stretch:</p>
<ul>
<li>Weeks 1–2: HTML and CSS basics — build a static page about anything.</li>
<li>Weeks 3–6: JavaScript fundamentals, practising in the browser: make buttons respond, change text on the page, build a small to-do list that remembers items.</li>
<li>Weeks 7–12: fetch data from a public API (weather, exchange rates, space launches) and display it on your page. Put the page online and send the link to a friend — that moment teaches more than another week of exercises.</li>
</ul>
<h2>Three objections, answered straight</h2>
<p><strong>"But Python is easier."</strong> For the first two weeks, yes — Python's indentation reads gentler than JavaScript's braces and semicolons, and its error messages are usually plainer. By month three this gap stops mattering; the hard part of programming is breaking problems into steps, and that is identical in both. Treat ease as a tie-breaker when the two questions disagree, not as the strategy.</p>
<p><strong>"Should I just learn both at once?"</strong> No. Splitting your hours doubles the time until your first finished project, and finished projects are what keep you going and what employers look at. Learn one until you can build small things without following a tutorial. The second language then takes weeks, not months, because loops, functions, and data structures transfer almost one-to-one. You are learning a dialect, not starting over.</p>
<p><strong>"What if I pick wrong?"</strong> You will know by about week six: you keep envying the other side's projects. Switching then costs roughly two weeks of re-orienting. Your understanding of programming itself does not reset. The genuinely expensive mistake is different — spending three months reading comparisons and writing nothing.</p>
<h2>Tonight, on paper</h2>
<p>Write down your answer to question 1: the one thing you want built in six months. Then tally your ten job ads for question 2. If both point to the same language, that is your first language, and the argument is over.</p>
<p>Then close the comparison tabs and write your first program tomorrow morning. A week of actual code will tell you more than another month of other people's opinions — including this one.</p>
]]></content:encoded></item><item><title><![CDATA[You Don't Need to Buy a Python Book — 6 Free Resources That Cover the Paid Shelf]]></title><description><![CDATA[Search "best Python book for beginners" and you will find a shelf of good books, most of them $30 to $50 each. The awkward fact is that almost everything…]]></description><link>https://aheadkit.hashnode.dev/you-don-t-need-to-buy-a-python-book-6-free-resources-that-cover-the-paid-shelf</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/you-don-t-need-to-buy-a-python-book-6-free-resources-that-cover-the-paid-shelf</guid><category><![CDATA[Python]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Tue, 03 Nov 2026 03:01:04 GMT</pubDate><content:encoded><![CDATA[<p>Search "best Python book for beginners" and you will find a shelf of good books, most of them $30 to $50 each. The awkward fact is that almost everything in them is also available free, legally, from the people and universities who teach Python best.</p>
<p>A beginner does not need to buy anything to learn Python well. What a beginner needs is the right free resources, used in the right order, with most of the time spent writing code. Here are the six that, together, cover the paid shelf. Every screenshot below is the actual starting page of that resource, as a learner would see it — no mockups.</p>
<h2>1. The official Python tutorial and docs (<a href="http://docs.python.org">docs.python.org</a>)</h2>
<p><strong>What it is:</strong> The tutorial and reference documentation written and maintained by the Python project itself.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/803724b4-c0bb-44f9-a415-99c74c7ac98e.png" alt="The official Python tutorial homepage on docs.python.org" style="display:block;margin:0 auto" />

<p><em>The official Python tutorial — the reference every other resource on this list quietly assumes you will end up reading.</em></p>
<p><strong>Format:</strong> Web documentation: a guided tutorial, then a full reference for the language and standard library.</p>
<p><strong>Time cost:</strong> The tutorial takes roughly 10 to 15 hours if you type the examples as you go. The docs themselves are not something you finish; you keep them open in a tab for years.</p>
<p><strong>Best at:</strong> Being right. If a blog post, a video, and the docs disagree, the docs win. Nothing covers the standard library as completely.</p>
<p><strong>Honest limit:</strong> It assumes you already think a little like a programmer, so true first-timers can find it dense and dry, with almost no exercises or coaching. Use it as your reference and fact-checker, not as your only teacher.</p>
<h2>2. Harvard CS50P (edX audit / <a href="http://cs50.harvard.edu">cs50.harvard.edu</a>)</h2>
<p><strong>What it is:</strong> Harvard's Introduction to Programming with Python, taught by David Malan's CS50 team. Free to audit on edX, and the lectures and problem sets are also free on Harvard's own site. Only the optional verified certificate costs money.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/8ae9f782-5168-441f-bb10-2489dd8881db.png" alt="Harvard CS50P course page showing the weekly lecture and problem set list" style="display:block;margin:0 auto" />

<p><em>The CS50P course page — ten weeks of lectures and problem sets, free to audit; only the optional certificate costs anything.</em></p>
<p><strong>Format:</strong> Video lectures (10 weekly units), problem sets with automatic checking, and a final project.</p>
<p><strong>Time cost:</strong> Harvard suggests 3 to 9 hours a week for 10 weeks — 40 to 90 hours in total for most self-learners.</p>
<p><strong>Best at:</strong> Teaching quality and problem sets. The lectures explain ideas clearly, and the problems make you write real programs from week one: functions, file input and output, regular expressions, unit tests, and a first taste of classes near the end.</p>
<p><strong>Honest limit:</strong> It stays in the terminal from start to finish. No web frameworks, no pandas, no apps with windows and buttons. If web development is your goal, CS50P is a strong first step, not the whole road.</p>
<h2>3. Python for Everybody — PY4E (<a href="http://py4e.com">py4e.com</a>)</h2>
<p><strong>What it is:</strong> Dr. Charles Severance's course from the University of Michigan: a free textbook, video lectures, and auto-graded assignments, all openly licensed and free on <a href="http://py4e.com">py4e.com</a> (also on Coursera, edX, and freeCodeCamp).</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/19c89ce9-a073-4c55-8f98-079f7237fbe9.png" alt="Python for Everybody homepage on py4e.com showing the free chapters and assignments" style="display:block;margin:0 auto" />

<p><em>Python for Everybody at</em> <a href="http://py4e.com"><em>py4e.com</em></a> <em>— the book, the lectures, and the graded assignments, free on one page.</em></p>
<p><strong>Format:</strong> Book chapters matched with short videos and auto-graded assignments in the browser.</p>
<p><strong>Time cost:</strong> Around 40 to 60 hours at your own pace, if you do the assignments properly instead of skimming.</p>
<p><strong>Best at:</strong> Absolute beginners, especially people without a maths or engineering background. The pace is gentle, the examples use real data early (files, web pages, databases), and the tone never makes you feel stupid.</p>
<p><strong>Honest limit:</strong> The gentle pace cuts both ways: if you already know another language, the first half feels slow. It also stops before advanced topics such as decorators, generators in depth, or type hints.</p>
<h2>4. Automate the Boring Stuff with Python (<a href="http://automatetheboringstuff.com">automatetheboringstuff.com</a>)</h2>
<p><strong>What it is:</strong> Al Sweigart's best-selling beginner book, published free to read online in full under a Creative Commons licence. The print copy is the same book people pay for.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/7e8d3f8e-3b19-4d56-8ef1-1ad9a46a25f6.png" alt="Automate the Boring Stuff with Python homepage with the full free table of contents" style="display:block;margin:0 auto" />

<p><em>Automate the Boring Stuff online — the complete book, chapter by chapter, free to read.</em></p>
<p><strong>Format:</strong> A complete book online, 24 chapters in the current edition, with practice questions and a small project at the end of most chapters.</p>
<p><strong>Time cost:</strong> Roughly 40 to 60 hours if you do the projects — most readers finish in four to six weeks of part-time study.</p>
<p><strong>Best at:</strong> Making Python useful fast. After the basics, it goes straight at real tasks: renaming and organising files, working with Excel sheets and PDFs, pulling data from web pages. This is the resource that turns "I am learning Python" into "Python saved me an afternoon."</p>
<p><strong>Honest limit:</strong> It teaches practical scripting, not computer science. Testing, program design, and object-oriented thinking get light coverage, and a few examples depend on websites that change over time.</p>
<h2>5. freeCodeCamp — Scientific Computing with Python</h2>
<p><strong>What it is:</strong> freeCodeCamp's Python certification path: a browser-based curriculum where you learn by building projects step by step, plus free full-length Python courses on YouTube. freeCodeCamp is a nonprofit; the courses carry no ads and no paywall.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/0f34d004-caff-4d70-a2bf-e102b580bf18.png" alt="freeCodeCamp curriculum page with Python Certification in the list" style="display:block;margin:0 auto" />

<p><em>freeCodeCamp's curriculum page — Python Certification sits in the same list as the web tracks, and signing in to save progress is free.</em></p>
<p><strong>Format:</strong> Interactive lessons in the browser, nothing to install, ending in a set of required projects for the certificate.</p>
<p><strong>Time cost:</strong> Plan on a few months part-time. freeCodeCamp estimates its certifications at around 300 hours, though many learners finish the core material faster.</p>
<p><strong>Best at:</strong> People who stall on video courses. You type code from the first lesson, feedback is immediate, and there is nothing to install — handy if your computer is old or shared.</p>
<p><strong>Honest limit:</strong> The explanations are thinner than CS50P or PY4E. Follow the steps passively and you can finish lessons without fully understanding them — pause, experiment, and break things on purpose to get the full value.</p>
<h2>6. Exercism — the Python track</h2>
<p><strong>What it is:</strong> A practice platform with a large set of Python exercises, automatic test feedback, and optional code review from human volunteer mentors. All of it, mentoring included, is free; Exercism is funded by donations.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/196dfb6f-a2ac-4f7b-a8f4-ad59926e8d70.png" alt="Exercism Python track page showing the exercise list" style="display:block;margin:0 auto" />

<p><em>The Exercism Python track — small exercises with tests to pass, and an optional human mentor at the end of them.</em></p>
<p><strong>Format:</strong> Small exercises, each with tests your code must pass, in the browser or in your own editor.</p>
<p><strong>Time cost:</strong> This one never really ends, which is the point. Budget two or three exercises a week, 20 to 60 minutes each, alongside your main course.</p>
<p><strong>Best at:</strong> Turning "I understood the lesson" into "I can write this myself." Solving small problems, reading other people's solutions afterwards, and getting a mentor's comments is the closest free equivalent of a classroom.</p>
<p><strong>Honest limit:</strong> It is practice, not teaching. Exercism assumes you can look concepts up yourself, and mentor replies can take a while because the mentors are volunteers.</p>
<h2>Use them in this order</h2>
<ol>
<li><strong>Pick one main course and finish it.</strong> PY4E if you want the gentlest start, CS50P if you want harder problem sets, freeCodeCamp if you only learn by typing. One. Not all three at once.</li>
<li><strong>Keep the official docs open from day one.</strong> Every time a course mentions a function or a module, spend two minutes reading its docs page. This habit pays off for years.</li>
<li><strong>From week three, add Exercism.</strong> Three exercises a week, every week, while the main course continues.</li>
<li><strong>After the main course, work through Automate the Boring Stuff's project chapters,</strong> skipping the basics you already know.</li>
<li><strong>Finish with a test you did not take.</strong> If you started with PY4E, do a few CS50P problem sets cold; if you started with CS50P, do freeCodeCamp's final projects. Unfamiliar formats show you what you actually retained.</li>
</ol>
<h2>When does paying for a book make sense?</h2>
<p>Honestly? Sometimes. You are never paying for secret knowledge — everything above is the same Python. You are paying for one voice, one sequence, everything edited to fit together, readable on paper or offline, with no tab-hopping between six sites. If that convenience keeps you studying, it is money reasonably spent. If money is tight, it is not a barrier: the free shelf above is not the cheap substitute. Used properly, it is the real thing.</p>
]]></content:encoded></item><item><title><![CDATA[Learn Python in 1 Hour a Day: A Realistic Week-by-Week Plan]]></title><description><![CDATA[One hour a day sounds too small to learn programming. It is not. One focused hour, repeated for twelve weeks, is 84 hours of practice — and 84 hours of…]]></description><link>https://aheadkit.hashnode.dev/learn-python-in-1-hour-a-day-a-realistic-week-by-week-plan</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/learn-python-in-1-hour-a-day-a-realistic-week-by-week-plan</guid><category><![CDATA[Python]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Mon, 02 Nov 2026 15:45:45 GMT</pubDate><content:encoded><![CDATA[<p>One hour a day sounds too small to learn programming. It is not. One focused hour, repeated for twelve weeks, is 84 hours of practice — and 84 hours of writing code will take you further than a month of watching tutorials ever will.</p>
<p>The catch: the hour has to be spent the right way. Here is the split that works, and the plan that follows from it.</p>
<h2>How to spend the hour</h2>
<p><strong>15 minutes: learn one small thing.</strong> One concept. Not a chapter, not a playlist — one idea, like "what a dictionary is" or "how a while loop exits."</p>
<p><strong>45 minutes: write code that uses it.</strong> Type programs by hand. Break them on purpose. Fix them. The learning happens in the fixing, not in the watching.</p>
<p>If you reverse this ratio — 45 minutes of video, 15 minutes of code — you will feel like you are learning and retain almost nothing. This is the single most common reason beginners quit: they confuse recognition ("I've seen this before") with ability ("I can write this myself").</p>
<p>One more rule: keep a mistakes notebook. Every bug that costs you more than five minutes goes in it, with one line about what was wrong. Review it every Sunday. Your notebook becomes a map of your own weak spots, which no course can give you.</p>
<h2>The 12-week plan</h2>
<p><strong>Weeks 1–2 — The basics, as programs</strong> Variables, numbers, strings, input and output, if/else. Write tiny programs daily: a temperature converter, a tip calculator, a number-guessing game, a program that counts the vowels in your name. By the end of week 2 you should have written about 15 small programs. Small is fine. Finished is what matters.</p>
<p><strong>Weeks 3–4 — Data and loops</strong> Lists, dictionaries, for loops, while loops. Programs: a shopping list that adds and removes items, a word-frequency counter for a paragraph of text, a contact book stored in a dictionary. This is where most people stall, because loops feel abstract. They stop feeling abstract after the tenth program, not before.</p>
<p><strong>Weeks 5–6 — Functions, files, and errors</strong> Break your programs into functions. Read and write text files. Learn try/except by deliberately feeding your programs bad input. Project for week 6: a personal expense tracker that saves entries to a file and prints a weekly total. It will be ugly. Ship it anyway.</p>
<p><strong>Weeks 7–8 — Object-oriented basics</strong> Classes, objects, methods — only as much as you need to model simple things: a BankAccount class, a Student class with grades. Do not spend a month here. Two weeks is enough for the fundamentals; depth comes later, when a real project demands it.</p>
<p><strong>Weeks 9–10 — The standard library and a real project</strong> Work with dates, random, os, and JSON. Then build one complete project start to finish: a quiz app, a file organizer that sorts your downloads folder, a simple web scraper for a page you actually read. One finished project teaches more than five half-finished ones.</p>
<p><strong>Weeks 11–12 — Write from memory</strong> Now change the exercise. Take programs you wrote in weeks 1–6, close them, and write them again from memory. Where you get stuck is exactly what you never learned — go back and fix only that. Finish the twelve weeks by re-writing your week 6 expense tracker from scratch. Compare the two versions. The difference is your progress, made visible.</p>
<h2>What to honestly expect at the end</h2>
<p>After twelve weeks of this you will not be a Python expert, and anyone who promises that in 84 hours is selling something. What you will have:</p>
<ul>
<li>The fundamentals, genuinely in your fingers, not just in your notes</li>
<li>About 50–60 programs you wrote yourself</li>
<li>Two finished projects</li>
<li>The ability to sit down and write a small program without a tutorial open</li>
</ul>
<p>That is a real foundation. From there, interviews, bigger projects, or a second language all become much easier — because the hard part was never the syntax. It was learning how to learn it. One hour a day, spent writing code, is how that happens.</p>
]]></content:encoded></item><item><title><![CDATA[Python Interview Prep: Practise With Programs You Can Run, Not Just Read]]></title><description><![CDATA[Most Python interview prep fails in the same place. You read about Fibonacci, decorators and two-sum for a week. Then the interviewer opens a blank editor…]]></description><link>https://aheadkit.hashnode.dev/python-interview-prep-practise-with-programs-you-can-run-not-just-read</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/python-interview-prep-practise-with-programs-you-can-run-not-just-read</guid><category><![CDATA[Python]]></category><category><![CDATA[interview]]></category><category><![CDATA[Beginner Developers]]></category><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Sun, 01 Nov 2026 04:08:49 GMT</pubDate><content:encoded><![CDATA[<p>Most Python interview prep fails in the same place. You read about Fibonacci, decorators and two-sum for a week. Then the interviewer opens a blank editor and says: write it. Reading and writing are different skills, and interviews test the second one.</p>
<p>So when I put together <a href="https://aheadkit.me/product/python-interview-prep">Python Interview Prep</a>, I built it around writing: 140 complete programs in five rounds (warm-ups, logic classics, core Python, OOP, job-level problems) and 100 interview questions. Every program is printed with the real output it produces — each one was actually run, so what you see is what your screen will show.</p>
<p>Here are three programs from the book, exactly as they appear in it.</p>
<h2>Program 24 — Print the Fibonacci Sequence With a Loop</h2>
<p><strong>What the interviewer is testing:</strong> Loop control and updating two variables in step. The simultaneous assignment <code>a, b = b, a + b</code> is the line they are listening for.</p>
<pre><code class="language-python">count = 10
a = 0
b = 1
print("Fibonacci sequence:")
for i in range(count):
    print(a, end=" ")
    a, b = b, a + b
print()
</code></pre>
<p>Output:</p>
<pre><code>Fibonacci sequence:
0 1 1 2 3 5 8 13 21 34
</code></pre>
<h2>Program 26 — Check Whether a Number Is an Armstrong Number</h2>
<p><strong>What the interviewer is testing:</strong> Turning a number into digits, and raising each digit to the right power. The classic trap is using power 3 for every number — the power is the number of digits.</p>
<pre><code class="language-python">number = 153
digits = str(number)
power = len(digits)
total = 0
for ch in digits:
    total = total + int(ch) ** power
print("Number:", number)
if total == number:
    print(number, "is an Armstrong number.")
else:
    print(number, "is not an Armstrong number.")
</code></pre>
<p>Output:</p>
<pre><code>Number: 153
153 is an Armstrong number.
</code></pre>
<h2>Program 60 — FizzBuzz</h2>
<p><strong>What the interviewer is testing:</strong> Whether you check the combined case first. If you test <code>% 3</code> before <code>% 15</code>, FizzBuzz never prints — that ordering mistake is the whole point of the question.</p>
<pre><code class="language-python">for number in range(1, 21):
    if number % 15 == 0:
        print("FizzBuzz")
    elif number % 3 == 0:
        print("Fizz")
    elif number % 5 == 0:
        print("Buzz")
    else:
        print(number)
</code></pre>
<p>Output:</p>
<pre><code>1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
</code></pre>
<h2>Read 25 programs free</h2>
<p>The first 25 programs plus 10 of the Part B questions are free to read online, no email needed: <a href="https://aheadkit.me/free-chapter/python-interview-prep">aheadkit.me/free-chapter/python-interview-prep</a></p>
<p>The full book (135 pages, $7.99, one payment) adds rounds 2–5 — comprehensions, decorators, generators, OOP, threads, async, and the algorithm problems that decide offers — plus all 100 questions and a 7-day practice plan for when the interview is already booked: <a href="https://aheadkit.me/product/python-interview-prep">Python Interview Prep on AheadKit</a></p>
<p>If you have never written Python at all, this book will be too much, too fast — it assumes variables, loops and functions. Start with a beginner book first, then come back and sharpen up.</p>
<p>Good luck in the interview. Practise by writing, and the blank editor stops being the scary part.</p>
]]></content:encoded></item><item><title><![CDATA[Python vs JavaScript: Which Should You Learn First?]]></title><description><![CDATA[It is the most common beginner question in programming, and most answers are tribal. Here is a fair comparison: what each language does all day, where each…]]></description><link>https://aheadkit.hashnode.dev/python-vs-javascript-which-should-you-learn-first</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/python-vs-javascript-which-should-you-learn-first</guid><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Fri, 30 Oct 2026 19:27:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/9153a9a6-71db-4aae-b656-9e162ad66029.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>It is the most common beginner question in programming, and most answers are tribal. Here is a fair comparison: what each language does all day, where each one trips beginners, and the kind of work waiting at the end of each path.</em></p>
<p><em>Originally published at <a href="https://aheadkit.me/guides/python-vs-javascript-which-to-learn-first/">AheadKit</a>.</em></p>
<p><strong>Python</strong> vs <strong>JavaScript</strong></p>
<h2>The short version</h2>
<p>Learn <strong>JavaScript</strong> first if you want to build things that run in a browser: websites, web apps, interactive pages. Learn <strong>Python</strong> first if you want to work with data, automate boring tasks on your own computer, or head toward AI tools and backend services. Everything below is detail. If that one paragraph already settles it, stop reading and start.</p>
<h2>What each language does all day</h2>
<p>JavaScript's home is the web page. Every button that responds, every form that checks itself, every chart that redraws runs on it, because browsers speak JavaScript and nothing else. With Node it also works on servers, so one language can cover a whole web product. A working JavaScript programmer spends the day building interfaces and the code behind them.</p>
<p>Python's home is everywhere else. It renames ten thousand files, pulls numbers out of spreadsheets, talks to databases, serves the back end of web apps through frameworks like Flask, and runs most of the data and machine-learning work you read about. A working Python programmer spends the day moving and processing information.</p>
<p>Notice the overlap: both can build the server side of a website, and both appear in each other's territory. The difference is which side of the fence each one calls home.</p>
<h2>Where each one trips beginners</h2>
<ul>
<li><strong>Python trips you gently, then all at once.</strong> The first weeks are famously friendly: code reads close to English and the feedback is instant. The stumble comes later, around installing other people's packages and virtual environments, where setup errors replace code errors. Indentation matters in Python, which beginners resent for a week and then stop noticing.</li>
<li><strong>JavaScript trips you early, then rewards you.</strong> You need no installation at all: open a browser, open its console, and you are programming, with results on screen in seconds. But the language has more moving parts at the start: the page structure (the DOM), events, and code that runs later instead of in order. Week three in JavaScript is harder than week three in Python. Month three is about even.</li>
</ul>
<h2>Side by side</h2>
<table>
<thead>
<tr>
<th></th>
<th>Python</th>
<th>JavaScript</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Runs where</strong></td>
<td>Your computer, servers, data pipelines</td>
<td>Every browser, plus servers via Node</td>
</tr>
<tr>
<td><strong>First visible result</strong></td>
<td>A script that does a real chore for you</td>
<td>A web page that moves and responds</td>
</tr>
<tr>
<td><strong>Setup needed</strong></td>
<td>Install Python, learn pip later</td>
<td>None. The browser is the workshop</td>
</tr>
<tr>
<td><strong>Typical first jobs</strong></td>
<td>Data analysis, automation, backend, QA, AI tooling</td>
<td>Front-end and full-stack web development</td>
</tr>
<tr>
<td><strong>Hardest early idea</strong></td>
<td>How programs are organised: functions, then classes</td>
<td>Asynchronous code and the DOM</td>
</tr>
<tr>
<td><strong>If you stop after one book</strong></td>
<td>You can automate your own work and query data</td>
<td>You can build and publish a working website</td>
</tr>
</tbody></table>
<h2>The jobs question, answered plainly</h2>
<p>Web development hires the most beginners, and on the web JavaScript is not optional, so JavaScript has the wider front door. Python's doors are data analyst and data-adjacent roles, automation and testing jobs, and backend work, where it often arrives as the second requirement next to SQL. Neither language is a bad bet. What decides is your local market, which you can measure yourself in ten minutes: search your target job title on the board you would apply on, read ten ads, and count how often each language is named. Our <a href="https://aheadkit.me/guides/best-programming-language-to-learn-first/">first-language guide</a> walks through that tie-breaker in full.</p>
<h2>One more thing nobody mentions</h2>
<p>Whichever you pick, you are not picking forever, and you are not even picking the hard part. Variables, loops, functions and the habit of breaking a problem into steps are the same in both; the second language typically takes a quarter of the time the first one did. Most working web developers end up with JavaScript plus a second language, and most data people end up with Python plus SQL. The first book is a starting point, not a marriage. If timelines help, the <a href="https://aheadkit.me/guides/how-long-does-it-take-to-learn-python/">Python timeline guide</a> shows what the months actually look like at an hour a day.</p>
<h2>Both paths, on one shelf</h2>
<p><em>Python for Everyone</em> is 172 pages and 26 chapters with 10 projects, ending at work you can show an employer, for $3.99. <em>JavaScript for Everyone</em> is 221 pages and 22 chapters with 9 projects across the browser and Node, for $2.99. Both product pages list every chapter before you pay, and both carry a 7-day money-back guarantee. If you already know a second language is coming later, the All 11 Programming Books bundle holds both, plus nine more, for $12.99.</p>
<p>See <a href="https://aheadkit.me/product/python-for-everyone/">Python for Everyone — $3.99</a> or <a href="https://aheadkit.me/product/javascript-for-everyone/">JavaScript for Everyone — $2.99</a>.</p>
<p>If you are leaning Python, you can <a href="https://aheadkit.me/free-sample/read/">read Chapters 1–3 of Python for Everyone free online</a> and judge the writing before you spend anything.</p>
<p>And if you already know you will want both languages eventually, the <a href="https://aheadkit.me/programming-books-bundle/">All 11 Programming Books bundle</a> is the cheapest way to get them together.</p>
]]></content:encoded></item><item><title><![CDATA[How Long Does It Take to Learn Python?]]></title><description><![CDATA[The honest answer depends on which finish line you mean. At an hour a day, most beginners write useful Python within about six weeks, feel at home in the…]]></description><link>https://aheadkit.hashnode.dev/how-long-does-it-take-to-learn-python</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/how-long-does-it-take-to-learn-python</guid><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Fri, 30 Oct 2026 19:22:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/1f441d37-c107-41e6-9422-e65a7a5ce7c6.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>The honest answer depends on which finish line you mean. At an hour a day, most beginners write useful Python within about six weeks, feel at home in the language in three to four months, and need six to nine months before they have projects worth showing an employer.</em></p>
<p><em>Originally published at <a href="https://aheadkit.me/guides/how-long-does-it-take-to-learn-python/">AheadKit</a>.</em></p>
<h2>Three finish lines, three timelines</h2>
<p>Most arguments about how long <strong>Python</strong> takes come from people using the same words for different finish lines. Separate them and the numbers stop contradicting each other:</p>
<table>
<thead>
<tr>
<th>Finish line</th>
<th>At 45–60 min a day</th>
<th>At 15 min a day</th>
</tr>
</thead>
<tbody><tr>
<td><strong>First useful scripts</strong> — read a file, rename 200 photos, total up a spreadsheet export</td>
<td>4–6 weeks</td>
<td>3–4 months</td>
</tr>
<tr>
<td><strong>Comfortable with the language</strong> — functions, lists, dictionaries, files and classes feel normal, and error messages stop being scary</td>
<td>3–4 months</td>
<td>9–12 months</td>
</tr>
<tr>
<td><strong>Work you can show an employer</strong> — two or three finished projects, built without following a tutorial line by line</td>
<td>6–9 months</td>
<td>18 months or more</td>
</tr>
</tbody></table>
<p>Notice what the table is really saying: the daily minutes matter more than talent. Fifteen minutes a day still gets there. It just gets there next year, and most people quit somewhere around month four, which is why the slow track has such a poor reputation.</p>
<h2>A realistic 12-week plan</h2>
<p>This is the plan behind the middle row of the table. It assumes an hour a day, six days a week, and it follows the order our own book uses, because that order works: each idea arrives just before you need it.</p>
<ul>
<li><strong>Weeks 1–2:</strong> install Python, write your first scripts. Variables, numbers, strings, printing and reading input. End the fortnight with a tip calculator and a number-guessing game.</li>
<li><strong>Weeks 3–4:</strong> conditionals and loops. Programs that decide and repeat: a quiz with a score, a password checker, FizzBuzz without peeking.</li>
<li><strong>Weeks 5–6:</strong> functions, then the data containers: lists, tuples, sets and dictionaries. This is the point where scripts stop being toys. Build a contact book that stores names in a dictionary.</li>
<li><strong>Weeks 7–8:</strong> working with files and handling errors without the program falling apart. Your contact book now saves to disk and survives being closed.</li>
<li><strong>Weeks 9–10:</strong> classes and objects, then modules, pip and virtual environments, so you can use code other people wrote instead of rebuilding everything.</li>
<li><strong>Weeks 11–12:</strong> one project, start to finish, on your own plan. An expense tracker that reads a CSV file and prints monthly totals is a good size. Add a small pytest test or two so you trust it.</li>
</ul>
<p>After week 12, stop studying topics and start finishing things. A second and third project teach more than another pass over the syntax, and they are what an employer can actually look at.</p>
<blockquote>
<p>Inside the book the plan above is built on: short explanations, then code you type yourself. Every program in it was run and checked before the book shipped.</p>
</blockquote>
<h2>What actually slows beginners down</h2>
<ul>
<li><strong>Tutorial hopping.</strong> Five half-finished courses teach the first two chapters five times. One book or course, finished, beats all of them.</li>
<li><strong>Watching instead of typing.</strong> Code you have only seen feels familiar right up until you face a blank file. Type every example. Break it on purpose and fix it.</li>
<li><strong>Fearing error messages.</strong> A traceback is a set of directions, not a verdict. Reading them calmly, from the last line up, is a skill in itself, and it arrives around week six if you practise it.</li>
<li><strong>Skipping files and classes.</strong> They look optional early on. Then your first real project needs both, and the gap shows up all at once.</li>
<li><strong>Comparing timelines.</strong> Someone online learned Python in six weeks because they already knew Java. Their clock is not your clock.</li>
</ul>
<h2>The answer, without the fluff</h2>
<p>Nobody finishes learning Python; working programmers still look things up daily. The finish line worth aiming at is the second row of the table: you sit down with a problem, no tutorial open, and you build something that works. At an hour a day that is three to four months away, and the 12 weeks above will get you most of the way there. The remaining distance is projects, and projects are the enjoyable part anyway.</p>
<h2>The book this plan is built on</h2>
<p><em>Python for Everyone</em> follows this exact path in 172 pages: 26 chapters from variables to a Tkinter GUI chapter, plus 10 step-by-step projects. Every program in it was run and checked before the book shipped, and the product page lists every chapter, so you can compare the plan above against the real contents before you spend anything. It costs $3.99, arrives by email in minutes, and carries a 7-day money-back guarantee.</p>
<p>See <a href="https://aheadkit.me/product/python-for-everyone/">Python for Everyone — $3.99</a>.</p>
<p>You can <a href="https://aheadkit.me/free-sample/read/">read Chapters 1–3 of Python for Everyone free online</a> before you decide whether the full book is for you.</p>
<p>If Python is only the first language on your list, the <a href="https://aheadkit.me/programming-books-bundle/">All 11 Programming Books bundle</a> collects Python with ten other beginner books for $12.99.</p>
]]></content:encoded></item><item><title><![CDATA[Best Python Books for Beginners (2026): Five Honest Options Compared]]></title><description><![CDATA[Five books account for nearly every "which Python book should I buy?" answer on the internet. This page compares them on the numbers that matter — price…]]></description><link>https://aheadkit.hashnode.dev/best-python-books-for-beginners-2026-five-honest-options-compared</link><guid isPermaLink="true">https://aheadkit.hashnode.dev/best-python-books-for-beginners-2026-five-honest-options-compared</guid><dc:creator><![CDATA[Abdus Salam]]></dc:creator><pubDate>Fri, 30 Oct 2026 19:21:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae4ec5d4e14ac3dd14cd7fc/7ef6bad3-ccb3-4e60-b35d-1cbda31582ad.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Five books account for nearly every "which Python book should I buy?" answer on the internet. This page compares them on the numbers that matter — price, pages, projects and pace — and says plainly where each one beats the others, including where each one beats ours.</em></p>
<p><em>Originally published at <a href="https://aheadkit.me/guides/best-python-books-for-beginners/">AheadKit</a>.</em></p>
<p><strong>Disclosure:</strong> We publish one of the books on this list (Python for Everyone, from AheadKit). That bias is exactly why every price and page count below was checked against the publisher's or a major retailer's page on 30 October 2026, and why the verdicts go against our own book where the facts say they should. If you only read one section, read "Where each book genuinely wins."</p>
<h2>The five books at a glance</h2>
<table>
<thead>
<tr>
<th>Book</th>
<th>Price (checked 30 Oct 2026)</th>
<th>Pages</th>
<th>Projects &amp; practice</th>
<th>Format</th>
<th>Best for</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Python Crash Course</strong>, 3rd ed. — Eric Matthes (No Starch, 2023)</td>
<td>$49.99 list (paperback)</td>
<td>552</td>
<td>Two halves: fundamentals, then three real projects (a game, data visualisation, a web app) plus exercises throughout</td>
<td>Paperback + ebook</td>
<td>The default recommendation — deepest beginner track record there is</td>
</tr>
<tr>
<td><strong>Automate the Boring Stuff with Python</strong>, 3rd ed. — Al Sweigart (No Starch, 2025)</td>
<td>$59.99 (print + ebook editions)</td>
<td>672</td>
<td>Practical automation scripts from early on; the 2nd edition is free to read online in full</td>
<td>Paperback + ebook; 2nd ed. free online</td>
<td>People who want useful scripts fast and learn by doing real tasks</td>
</tr>
<tr>
<td><strong>Head First Python</strong>, 3rd ed. — Paul Barry (O'Reilly, 2023)</td>
<td>$65.99 list (paperback)</td>
<td>663</td>
<td>Visual puzzles, exercises and conversation-style teaching throughout</td>
<td>Paperback + ebook</td>
<td>Visual learners who find plain textbooks dry</td>
</tr>
<tr>
<td><strong>Python for Everybody</strong> — Charles Severance (2016, Creative Commons)</td>
<td>Free (official PDF + course); print about $9.99–$10.99</td>
<td>About 247</td>
<td>Exercises per chapter; built around data handling — files, networks, databases</td>
<td>Free PDF / web / video course</td>
<td>Zero budget, or a data-flavoured first course</td>
</tr>
<tr>
<td><strong>Python for Everyone</strong> — AheadKit (ours)</td>
<td>$3.99</td>
<td>172</td>
<td>26 chapters, 10 step-by-step projects, an exercise set closing every chapter</td>
<td>PDF, emailed on purchase</td>
<td>A complete first book at the price of a coffee, readable this week</td>
</tr>
</tbody></table>
<p>Those recommendation shares tell the same story: in one large analysis of 2,000 book recommendations across 70 "best Python books" lists, Python Crash Course took 33.3% of all mentions, Head First Python 25.4% and Automate the Boring Stuff 23.7%. The famous three are famous for a reason. The honest question is what you personally need — and what you should pay for it.</p>
<h2>Each book, honestly</h2>
<h3>Python Crash Course (No Starch)</h3>
<p>The most recommended beginner Python book in the world, and the recommendation is earned. The first half teaches the language cleanly; the second half builds three substantial projects, which is more project weight than any other book here. If your budget stretches to about $50 and you want the single safest choice — the one with a decade of readers, errata, answers and community help behind it — this is the book, and it is not close. What it costs you: about $50, 552 pages of commitment, and a pace that assumes you are settling in for months.</p>
<h3>Automate the Boring Stuff with Python (No Starch)</h3>
<p>The practical one. Sweigart skips much of the theory-first ordering and gets you automating real, boring tasks — files, spreadsheets, the web — early, which is why office workers love it. The 3rd edition (2025) lists at $59.99; the complete 2nd edition remains free to read on the author's site, which is the single most generous offer on this list. Choose it if your goal is useful scripts for work rather than a ground-up education — and start with the free 2nd edition before spending anything.</p>
<h3>Head First Python (O'Reilly)</h3>
<p>The visual one: 663 pages built on pictures, puzzles and a deliberately chatty style, at a $65.99 list price — the most expensive route here. People who bounce off dense textbooks often finish this one, and finishing beats owning. People who like direct, compact writing often find the style noisy. It is the most taste-dependent book on the list; look at a preview before you pay for it.</p>
<h3>Python for Everybody (free)</h3>
<p>The free one, and genuinely good: Charles Severance's course-book is Creative Commons, the PDF and the full course cost nothing at py4e.com, and its data-handling angle (files, networks, databases) is a sensible first map of Python. It is also the oldest book here (2016), written in a course's voice rather than a book's, and it will not coach your practising the way the project books do. At $0 it never needs justifying — if money is the question, this is an answer.</p>
<h3>Python for Everyone (ours — AheadKit)</h3>
<p>Ours, so weigh this paragraph accordingly. It is a complete first book — 26 chapters from installing Python to classes, files, Flask, pandas and Tkinter, 10 step-by-step projects, exercises closing every chapter — in 172 pages for $3.99 as a PDF. Its honest advantages are price and pace: a tenth of Python Crash Course's list price, and short enough to finish in weeks rather than months. Chapters 1–3 are <a href="https://aheadkit.me/free-sample/read/">free to read in full</a>, so the writing can speak for itself. Its honest limits: it is a PDF from a small publisher, with no print edition, no decade of community around it, and less depth than Crash Course's project half. If those matter more than price, buy Crash Course with our blessing.</p>
<h2>Where each book genuinely wins</h2>
<ul>
<li><strong>Deepest, safest education:</strong> Python Crash Course. Nobody sensible argues otherwise.</li>
<li><strong>Useful work scripts, soonest:</strong> Automate the Boring Stuff — and its free 2nd edition is the best free starting point on this list.</li>
<li><strong>Most engaging for visual learners:</strong> Head First Python, if the style suits you.</li>
<li><strong>Zero budget, data-flavoured:</strong> Python for Everybody. Free is a complete answer to "which book can I afford?"</li>
<li><strong>Complete book per dollar, finishable fastest:</strong> Python for Everyone — $3.99 for the whole arc, with the first three chapters free so you are not buying on faith.</li>
</ul>
<h2>What I would buy, by situation</h2>
<ul>
<li><strong>"I have about $50 and want the proven path."</strong> Python Crash Course. It earned its position.</li>
<li><strong>"I want Python for my office job."</strong> Start with the free 2nd edition of Automate the Boring Stuff online; buy the 3rd edition only if you want the updated print.</li>
<li><strong>"Textbooks bore me by page ten."</strong> Head First Python — preview it first; the style is the product.</li>
<li><strong>"I have no budget at all."</strong> Python for Everybody at py4e.com. Do not pirate the others; the free route here is legitimately good.</li>
<li><strong>"I want to finish a complete book this month, cheaply."</strong> <a href="https://aheadkit.me/product/python-for-everyone">Python for Everyone</a> — read the <a href="https://aheadkit.me/free-sample/read/">free chapters</a> first and decide with evidence. Or take it inside the <a href="https://aheadkit.me/programming-books-bundle/">All 11 Programming Books bundle</a> ($12.99 for eleven beginner books) if more languages are on your list.</li>
</ul>
<h2>Frequently asked questions</h2>
<p><strong>What is the single best Python book for a total beginner?</strong>
For most people with a normal book budget, Python Crash Course — it is the most recommended beginner Python book across the lists we analysed (33.3% of 2,000 recommendations), and its fundamentals-plus-projects structure is the safest complete path. If your budget is small, Python for Everyone covers the same ground for $3.99, and Python for Everybody is free.</p>
<p><strong>Is Python Crash Course worth $49.99 when free books exist?</strong>
If you will finish it, yes: its project half and its community (a decade of answered questions) are things the free options do not match. If you are unsure Python is for you, start free — Python for Everybody or the free Automate the Boring Stuff 2nd edition — and spend money only once you know you will stick with it.</p>
<p><strong>Which Python book is best for automation and office work?</strong>
Automate the Boring Stuff with Python. It is organised around practical tasks — files, spreadsheets, PDFs, the web — rather than language theory, and the complete 2nd edition is free to read online, so you can start today at $0.</p>
<p><strong>Are the cheap $3–$4 Python ebooks any good?</strong>
Some are complete books and some are pamphlets with a cover. The check that separates them: a full table of contents (ours runs 26 chapters and 10 projects), real page count (172 pages), exercises, and free chapters you can read before paying. Judge any cheap book — including ours — by its free sample, not its sales page.</p>
<p><strong>Should I buy one book or a bundle?</strong>
One book if you know your language: depth beats breadth at the start. A bundle earns its money when you already know a second language is coming — at $12.99 for eleven books, our <a href="https://aheadkit.me/programming-books-bundle/">programming books bundle</a> costs less than a quarter of one famous paperback, so the cost of the shelf being there when you need it is small.</p>
]]></content:encoded></item></channel></rss>