<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Hariom Sharma</title>
    <description>The latest articles on Forem by Hariom Sharma (@harryy2510).</description>
    <link>https://forem.com/harryy2510</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F616046%2F3e77768a-1d3e-4ae5-9e77-70fcd02cec13.jpeg</url>
      <title>Forem: Hariom Sharma</title>
      <link>https://forem.com/harryy2510</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/harryy2510"/>
    <language>en</language>
    <item>
      <title>Claude Moves Fast. Codex Ships.</title>
      <dc:creator>Hariom Sharma</dc:creator>
      <pubDate>Tue, 05 May 2026 00:00:07 +0000</pubDate>
      <link>https://forem.com/harryy2510/claude-moves-fast-codex-ships-4fmd</link>
      <guid>https://forem.com/harryy2510/claude-moves-fast-codex-ships-4fmd</guid>
      <description>&lt;p&gt;I gave two big coding tasks to both Claude and Codex.&lt;/p&gt;

&lt;p&gt;Claude finished in about one hour. Codex took about eight.&lt;/p&gt;

&lt;p&gt;That sounds like a clean win for Claude until you look at what came back. The Claude output was fast, confident, and useless. Bad assumptions, broken code, missing integration points, half-followed rules, and a shape that would make the codebase harder to maintain even if I patched it into working order.&lt;/p&gt;

&lt;p&gt;I threw it away.&lt;/p&gt;

&lt;p&gt;The Codex output took a full workday. It read the repo. It followed the local instructions. It reused existing patterns. It added tests. It ran checks. And when I ran the result the first time, it worked.&lt;/p&gt;

&lt;p&gt;That changed how I think about AI coding tools.&lt;/p&gt;

&lt;p&gt;I am not writing this as a casual weekend test. I have shipped three products in the last four months with AI doing a serious amount of the implementation work. I use these tools hard enough to burn through weekly limits on a $200/month subscription. This is still personal opinion, not a benchmark, but it is opinion built from long hours in real repos where broken code costs me time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude optimizes for motion. Codex optimizes for grounded work.&lt;/li&gt;
&lt;li&gt;Fast output is not fast delivery if you spend the next day cleaning it up.&lt;/li&gt;
&lt;li&gt;Codex's friction is annoying, but most of that friction is it reading, verifying, and respecting project rules.&lt;/li&gt;
&lt;li&gt;Superpowers improves both tools, but each agent follows the workflow differently.&lt;/li&gt;
&lt;li&gt;Claude's subagent defaults are useful, but delegation does not save you when the main agent is willing to invent around your codebase.&lt;/li&gt;
&lt;li&gt;For work I actually ship, I now trust the slower agent more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy767jpt49p31xzr08k5a.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy767jpt49p31xzr08k5a.webp" alt="A notebook diagram comparing a messy patch loop with a clean verified shipping path" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Claude feels like an engineer who wants to finish the ticket before lunch.&lt;/p&gt;

&lt;p&gt;Codex feels like an engineer who annoys you by reading every linked file before touching the code, then quietly hands you something that passes tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-hour trap
&lt;/h2&gt;

&lt;p&gt;The trap is simple: Claude makes progress visible very quickly.&lt;/p&gt;

&lt;p&gt;It edits. It assumes. It finds a path through your rules. It writes what it thinks should exist. It may use subagents aggressively. It may produce a lot of code fast. That feels productive while it is happening.&lt;/p&gt;

&lt;p&gt;Then you run it.&lt;/p&gt;

&lt;p&gt;The first failure is usually small. A type mismatch. A missing import. A component API that does not exist. A hook that follows a generic React pattern instead of your local data-fetching pattern.&lt;/p&gt;

&lt;p&gt;You ask for a fix. Claude patches the symptom. Now something else breaks.&lt;/p&gt;

&lt;p&gt;You ask why the original thing failed. Instead of tracing the root cause, it often jumps into another patch. The code starts collecting bandaids. The more you accept, the harder it becomes to tell what is necessary and what is agent residue.&lt;/p&gt;

&lt;p&gt;That is how a codebase becomes fragile. Not from one bad commit. From many plausible commits that never had to prove they matched the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The eight-hour tradeoff
&lt;/h2&gt;

&lt;p&gt;Codex is slower in a way that can be frustrating.&lt;/p&gt;

&lt;p&gt;For even simple work, it tends to read first. It opens the files. It checks instructions. It searches for existing patterns. It tries to understand why the current code is shaped the way it is. It is much less eager to introduce a new abstraction just because it can.&lt;/p&gt;

&lt;p&gt;To be fair, that eight-hour run was not an optimized Codex setup. I tested Codex almost as soon as I installed it. I only had Superpowers installed. I had not yet tuned my &lt;code&gt;AGENTS.md&lt;/code&gt;, wired a stronger agent workflow, or built the muscle memory for telling Codex when to split work.&lt;/p&gt;

&lt;p&gt;So a lot of the time was self-inflicted setup friction.&lt;/p&gt;

&lt;p&gt;Codex also tested after almost every meaningful change. That ate most of the wall-clock time. It would edit, run the relevant check, read the failure, patch, run again, and keep going. That is slow compared with an agent that writes a big diff and says "done." It is also why the final result worked.&lt;/p&gt;

&lt;p&gt;The other missing piece was default subagents. Claude tends to fan out work more aggressively. My Codex setup did not do that by default. Without explicit subagent instructions, more of the work stayed in the main thread, which made the run longer than it needed to be.&lt;/p&gt;

&lt;p&gt;That patience costs time.&lt;/p&gt;

&lt;p&gt;But the time is not empty. It is spent on the things that usually decide whether code survives first contact with the repo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this match the surrounding modules?&lt;/li&gt;
&lt;li&gt;Is there already a helper for this?&lt;/li&gt;
&lt;li&gt;Are there project rules that override the obvious solution?&lt;/li&gt;
&lt;li&gt;Is the failure the root cause, or just the first visible symptom?&lt;/li&gt;
&lt;li&gt;What is the smallest check that proves this change worked?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwe4pvef7vawlnmxjp292.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwe4pvef7vawlnmxjp292.webp" alt="A notebook workflow diagram showing repo reading, searching, editing, checking, fixing, and shipping" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I do not want a coding agent that wins the stopwatch and loses the branch.&lt;/p&gt;

&lt;p&gt;I want the branch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Search behavior is a bigger difference than people admit
&lt;/h2&gt;

&lt;p&gt;Claude tends to work from model knowledge unless you push it to fetch current information.&lt;/p&gt;

&lt;p&gt;For stable things, that is fine. For modern SDKs, AI tools, product docs, pricing, CLI behavior, framework releases, or anything that may have changed last month, it is dangerous.&lt;/p&gt;

&lt;p&gt;I do not want an agent confidently writing against a six-month-old mental model of a library.&lt;/p&gt;

&lt;p&gt;Codex is much more willing to say: this might have changed, search first. The installed CLI even exposes &lt;code&gt;--search&lt;/code&gt;, and the current Codex docs have first-class pages for &lt;a href="https://developers.openai.com/codex/rules" rel="noopener noreferrer"&gt;rules&lt;/a&gt; and &lt;a href="https://developers.openai.com/codex/subagents" rel="noopener noreferrer"&gt;subagents&lt;/a&gt;. The exact feature surface changes fast, which is the point: for fast-moving tools, searching is not optional polish. It is correctness.&lt;/p&gt;

&lt;p&gt;This is one of the reasons Codex feels slow. It spends time making sure the premise is still true.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft58t4s7hg6z6y4eawyvg.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft58t4s7hg6z6y4eawyvg.webp" alt="A notebook diagram showing stale assumptions moving through search and verification into a clean implementation" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules matter more than intelligence
&lt;/h2&gt;

&lt;p&gt;My biggest complaint about Claude is not that it is dumb. It is not. It is powerful.&lt;/p&gt;

&lt;p&gt;The problem is that it is too willing to route around constraints.&lt;/p&gt;

&lt;p&gt;If a repo says "do not edit env files," I do not want the agent to decide that &lt;code&gt;.env.example&lt;/code&gt; is close enough to documentation. If a repo says "do not add scripts unless asked," I do not want the agent to add a convenience script because it makes its own workflow nicer. If I ask "why is this not working," I do not want three patch attempts before the root cause is found.&lt;/p&gt;

&lt;p&gt;Codex is slower partly because the rules stay heavy in its head.&lt;/p&gt;

&lt;p&gt;It treats instructions as part of the job, not as decoration. That matters in real repos, where the fastest fix is often the fix that breaks a migration, a hook, a deploy job, or another engineer's in-progress work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Superpowers fits
&lt;/h2&gt;

&lt;p&gt;I also use &lt;a href="https://github.com/obra/superpowers" rel="noopener noreferrer"&gt;Superpowers&lt;/a&gt; with both Claude and Codex.&lt;/p&gt;

&lt;p&gt;Superpowers is a workflow plugin by Jesse Vincent. The pitch is simple: do not let the agent jump straight into coding. Make it brainstorm, plan, route work, use TDD when appropriate, review the result, and verify before claiming success. The &lt;a href="https://claude.com/plugins/superpowers" rel="noopener noreferrer"&gt;official Claude marketplace page&lt;/a&gt; describes it as a skills framework for brainstorming, subagent development, debugging, TDD, and skill authoring. The GitHub repo also documents Codex installation, so this is not a Claude-only idea.&lt;/p&gt;

&lt;p&gt;Claude does pretty well with Superpowers. It understands the workflow and can move fast once the plan exists. The problem is that Claude still feels like it is deciding when the workflow matters. Sometimes it invokes the right skill. Sometimes it treats the skill like advice. Sometimes it decides the task is obvious and starts moving.&lt;/p&gt;

&lt;p&gt;Codex behaves differently. If the rules say "use Superpowers to route this," Codex is much more likely to actually do that. It will invoke the routing step, check which skill applies, and follow the workflow even when I personally think the task could have been done in two edits. That strictness was part of why my first Codex run took so long: Superpowers wanted routing and planning, and Codex treated that as a rule, not a suggestion.&lt;/p&gt;

&lt;p&gt;That is both good and bad.&lt;/p&gt;

&lt;p&gt;For big tasks, I want that discipline. Route first. Plan first. Use subagents when the work is independent. Review before declaring done. That is exactly where Superpowers shines.&lt;/p&gt;

&lt;p&gt;For small tasks, it can be overkill. If I ask for a copy tweak or a one-line bug fix, I do not always need a methodology. This is the same tradeoff as Codex itself: the discipline that makes it reliable on large work can feel heavy on tiny work.&lt;/p&gt;

&lt;p&gt;Still, if I have to choose which failure mode I prefer, I will take "too much process" over "fast wrong code" almost every time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwacwnvnz1md3u5zoildp.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwacwnvnz1md3u5zoildp.webp" alt="A notebook diagram showing shortcuts blocked by project rules and routed through planning, testing, review, and shipping" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Subagents are not magic
&lt;/h2&gt;

&lt;p&gt;Claude defaults harder toward subagents. Codex can use subagents too, but in my workflow I usually need to ask explicitly or encode it in &lt;code&gt;AGENTS.md&lt;/code&gt;. That lines up with the &lt;a href="https://developers.openai.com/codex/subagents" rel="noopener noreferrer"&gt;Codex subagents docs&lt;/a&gt;, which describe them as explicit helpers for larger tasks. On that first run, I had not done the tuning yet, so Codex paid the cost of doing too much sequentially.&lt;/p&gt;

&lt;p&gt;That is a real advantage for Claude. Subagents can isolate context, split work, and make large tasks move faster.&lt;/p&gt;

&lt;p&gt;But delegation only helps if the delegated work is bounded and grounded.&lt;/p&gt;

&lt;p&gt;If the parent agent makes bad assumptions, you get multiple workers implementing bad assumptions in parallel. That is not leverage. That is multiplication.&lt;/p&gt;

&lt;p&gt;Codex's default is more conservative. It does not fan work out as aggressively unless the task clearly benefits from it. Sometimes that costs time. Sometimes it prevents a mess.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczlfoelveefursjfmmuv.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fczlfoelveefursjfmmuv.webp" alt="A notebook diagram comparing bad assumptions multiplied across workers with bounded subagent lanes merging into a shipped result" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The real speed metric
&lt;/h2&gt;

&lt;p&gt;Here is the metric I care about now:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How long from prompt to shippable branch?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not prompt to diff.&lt;/p&gt;

&lt;p&gt;Not prompt to "I made the changes."&lt;/p&gt;

&lt;p&gt;Not prompt to a confident summary.&lt;/p&gt;

&lt;p&gt;Prompt to code I can ship without feeling like I need to audit every line for agent damage.&lt;/p&gt;

&lt;p&gt;On that metric, the one-hour Claude run was slower than the eight-hour Codex run because the Claude output had no path to trust. Every line needed suspicion. The Codex output had already done the boring work: inspect, edit, test, verify.&lt;/p&gt;

&lt;p&gt;That is why I keep using Codex even when it annoys me.&lt;/p&gt;

&lt;p&gt;I do not need the fastest agent.&lt;/p&gt;

&lt;p&gt;I need the one whose output I can ship with confidence.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://harryy.me/blog/claude-is-fast-codex-is-slow" rel="noopener noreferrer"&gt;harryy.me&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>programming</category>
      <category>codex</category>
    </item>
    <item>
      <title>I Built the Free Geo API Every App Eventually Needs</title>
      <dc:creator>Hariom Sharma</dc:creator>
      <pubDate>Thu, 30 Apr 2026 14:52:06 +0000</pubDate>
      <link>https://forem.com/harryy2510/i-built-the-free-geo-api-every-app-eventually-needs-13da</link>
      <guid>https://forem.com/harryy2510/i-built-the-free-geo-api-every-app-eventually-needs-13da</guid>
      <description>&lt;p&gt;Every app eventually needs boring location data.&lt;/p&gt;

&lt;p&gt;Country dropdowns. State selectors. City autocomplete. Timezone hints. IP-based defaults. The work is not hard, but the options are usually annoying: pay for an API, sign up for a free key with tiny limits, or self-host a database dump for a form field.&lt;/p&gt;

&lt;p&gt;I wanted the boring version: &lt;strong&gt;no key, no signup, no server to babysit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The API serves 252 countries, 5,024 states, 232,008 cities, 312 timezones, and 178 currencies&lt;/li&gt;
&lt;li&gt;Cloudflare's &lt;code&gt;request.cf&lt;/code&gt; gives caller location hints; D1 enriches them with full records&lt;/li&gt;
&lt;li&gt;SQLite FTS5 handles global search without Algolia or Elasticsearch&lt;/li&gt;
&lt;li&gt;The best product decision was deleting auth, API keys, and app-level rate limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9va2iaabo3lcapisi77e.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9va2iaabo3lcapisi77e.webp" alt="A notebook diagram showing app location needs flowing through a small geolocation API into clean location records" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why existing geo APIs are annoying
&lt;/h2&gt;

&lt;p&gt;Most geo APIs make static data feel dynamic.&lt;/p&gt;

&lt;p&gt;Country names, ISO codes, currencies, timezones, and state boundaries come from public datasets. Charging per request for a list of countries is a business model, not a technical requirement.&lt;/p&gt;

&lt;p&gt;Free APIs exist, but many still require registration before a developer can run one &lt;code&gt;curl&lt;/code&gt;. Some have tiny daily limits. Some are slow because every request goes back to the same origin.&lt;/p&gt;

&lt;p&gt;Self-hosting fixes control but adds chores: download dumps, import them, expose routes, write search, keep everything updated. For a country dropdown, that is a lot of infrastructure.&lt;/p&gt;

&lt;p&gt;The shape I wanted was simpler:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Why it mattered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No API key&lt;/td&gt;
&lt;td&gt;Let people try it before they care&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cached static data&lt;/td&gt;
&lt;td&gt;Countries and timezones do not change per request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real database queries&lt;/td&gt;
&lt;td&gt;JSON blobs are fine until search and pagination show up&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hostable&lt;/td&gt;
&lt;td&gt;A fork should not depend on my domains&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open data sources&lt;/td&gt;
&lt;td&gt;No mystery dataset hiding behind the API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That led to &lt;a href="https://developers.cloudflare.com/workers/" rel="noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt;, &lt;a href="https://developers.cloudflare.com/d1/" rel="noopener noreferrer"&gt;D1&lt;/a&gt;, and a small &lt;a href="https://hono.dev/" rel="noopener noreferrer"&gt;Hono&lt;/a&gt; router.&lt;/p&gt;




&lt;h2&gt;
  
  
  The architecture is intentionally small
&lt;/h2&gt;

&lt;p&gt;The public API is one &lt;a href="https://developers.cloudflare.com/workers/" rel="noopener noreferrer"&gt;Worker&lt;/a&gt; with one runtime dependency: &lt;a href="https://hono.dev/" rel="noopener noreferrer"&gt;&lt;strong&gt;Hono&lt;/strong&gt;&lt;/a&gt; for routing and CORS.&lt;/p&gt;

&lt;p&gt;There is no ORM, no API gateway, no auth service, and no external search cluster. The Worker talks to &lt;a href="https://developers.cloudflare.com/d1/" rel="noopener noreferrer"&gt;D1&lt;/a&gt; through a binding, serves static assets through &lt;a href="https://developers.cloudflare.com/workers/static-assets/" rel="noopener noreferrer"&gt;Workers Static Assets&lt;/a&gt;, and exposes the &lt;a href="https://spec.openapis.org/oas/v3.1.0.html" rel="noopener noreferrer"&gt;OpenAPI spec&lt;/a&gt; from the same deployment.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Host&lt;/th&gt;
&lt;th&gt;What the Worker returns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://geocoded.me" rel="noopener noreferrer"&gt;&lt;code&gt;geocoded.me&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Landing page, dashboard, explorer, docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://api.geocoded.me" rel="noopener noreferrer"&gt;&lt;code&gt;api.geocoded.me&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;JSON API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://geocoded.me/openapi.json" rel="noopener noreferrer"&gt;&lt;code&gt;/openapi.json&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://spec.openapis.org/oas/v3.1.0.html" rel="noopener noreferrer"&gt;OpenAPI 3.1&lt;/a&gt; document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://geocoded.me/postman.json" rel="noopener noreferrer"&gt;&lt;code&gt;/postman.json&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://geocoded.me/postman.json" rel="noopener noreferrer"&gt;Postman collection&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The important bit is not cleverness. It is drift control.&lt;/p&gt;

&lt;p&gt;The docs site and API ship together, so the documentation cannot lag behind a separate backend deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  What actually shipped?
&lt;/h2&gt;

&lt;p&gt;The API is mostly boring REST.&lt;/p&gt;

&lt;p&gt;The useful parts are the defaults: all list endpoints are paginated, every endpoint supports field selection, and static responses get long-lived cache headers.&lt;/p&gt;

&lt;p&gt;The first useful route is also the shortest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.geocoded.me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That returns the caller's Cloudflare location hints, then enriches them with full country, state, and city records when D1 can match them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cloudflare already has the first location signal
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developers.cloudflare.com/workers/runtime-apis/request/#incomingrequestcfproperties" rel="noopener noreferrer"&gt;Cloudflare adds a &lt;code&gt;cf&lt;/code&gt; object&lt;/a&gt; to inbound Worker requests. When available, it includes useful hints like country, region, city, coordinates, timezone, ASN, and colo.&lt;/p&gt;

&lt;p&gt;That is enough to answer "where is this request probably coming from?" It is not enough to power a product UI.&lt;/p&gt;

&lt;p&gt;Cloudflare might give you &lt;code&gt;country: "US"&lt;/code&gt; and &lt;code&gt;regionCode: "CA"&lt;/code&gt;. Geocoded turns that into full records: country name, flag emoji, currency, phone code, state name, timezone, and city metadata.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Response type&lt;/th&gt;
&lt;th&gt;Cache policy&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Countries, states, cities, timezones, currencies&lt;/td&gt;
&lt;td&gt;&lt;code&gt;public, max-age=31536000, immutable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Data changes through the pipeline, not per request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caller location&lt;/td&gt;
&lt;td&gt;&lt;code&gt;private, no-store&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Your IP location changes with VPNs, travel, and networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAPI/Postman docs&lt;/td&gt;
&lt;td&gt;Short public cache&lt;/td&gt;
&lt;td&gt;Useful to cache, but easier to refresh&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The city match is deliberately conservative: country + state + city name. It works well enough for a free API, but I would not pretend it is a parcel-grade geocoder.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why D1 was enough
&lt;/h2&gt;

&lt;p&gt;D1 gave me the part JSON files do not: &lt;strong&gt;queries&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Countries, states, cities, timezones, and currencies live in normal tables with indexes. Global search uses &lt;a href="https://www.sqlite.org/fts5.html" rel="noopener noreferrer"&gt;SQLite FTS5&lt;/a&gt;, so &lt;code&gt;?q=san&lt;/code&gt; can search across countries, states, and cities without an external search service.&lt;/p&gt;

&lt;p&gt;The whole schema is still small. The checked-in JSON output is under 70 MB, and the D1 database stays well below the current &lt;a href="https://developers.cloudflare.com/d1/platform/limits/" rel="noopener noreferrer"&gt;D1 storage limits&lt;/a&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Why D1 fit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pagination&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LIMIT&lt;/code&gt;, &lt;code&gt;OFFSET&lt;/code&gt;, and a simple opaque cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lookup routes&lt;/td&gt;
&lt;td&gt;Indexed country/state/city columns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full-text search&lt;/td&gt;
&lt;td&gt;FTS5 virtual table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-hosting&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;wrangler d1 create&lt;/code&gt;, migrations, seed script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No database server&lt;/td&gt;
&lt;td&gt;D1 is managed and bound directly to the Worker&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If this becomes a fuzzy geocoder, I would revisit the storage/search layer. For country/state/city lookup, D1 is enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  Field selection is the cheap GraphQL
&lt;/h2&gt;

&lt;p&gt;A full country record has a lot of fields: translations, neighbours, languages, driving side, literacy rate, timezone list, currency info, postal code metadata, and more.&lt;/p&gt;

&lt;p&gt;Most callers do not need all of that.&lt;/p&gt;

&lt;p&gt;So every endpoint accepts &lt;code&gt;?fields=&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request&lt;/th&gt;
&lt;th&gt;Payload intent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/countries?fields=name,iso2,emoji&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build a country selector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/countries/US?fields=name,capital,currency&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show a compact country profile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/?fields=ip,country,countryInfo.name,countryInfo.emoji&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enrich caller location without the full country object&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I considered GraphQL for about five minutes.&lt;/p&gt;

&lt;p&gt;Then I remembered what I actually wanted: comma-separated fields, dot notation for nested objects, and no new runtime dependency.&lt;/p&gt;




&lt;h2&gt;
  
  
  The data pipeline is the real product
&lt;/h2&gt;

&lt;p&gt;The API is small because the pipeline does the messy work before deploy time.&lt;/p&gt;

&lt;p&gt;The data comes from institutional sources: &lt;a href="https://www.geonames.org/" rel="noopener noreferrer"&gt;GeoNames&lt;/a&gt;, &lt;a href="https://cldr.unicode.org/" rel="noopener noreferrer"&gt;Unicode CLDR&lt;/a&gt;, &lt;a href="https://www.iana.org/time-zones" rel="noopener noreferrer"&gt;IANA&lt;/a&gt;, &lt;a href="https://www.six-group.com/en/products-services/financial-information/data-standards.html" rel="noopener noreferrer"&gt;ISO 4217&lt;/a&gt;, &lt;a href="https://www.naturalearthdata.com/" rel="noopener noreferrer"&gt;Natural Earth&lt;/a&gt;, &lt;a href="https://www.wikidata.org/" rel="noopener noreferrer"&gt;Wikidata&lt;/a&gt;, trusted boundary sources, and vendored CIA World Factbook demonyms.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Used for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GeoNames&lt;/td&gt;
&lt;td&gt;Countries, admin divisions, cities, coordinates, population, timezones&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CLDR&lt;/td&gt;
&lt;td&gt;Translations, currency symbols, GDP/literacy hints, week/time/measurement data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IANA&lt;/td&gt;
&lt;td&gt;Timezone IDs and country mappings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ISO 4217&lt;/td&gt;
&lt;td&gt;Currency codes, names, decimal digits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Natural Earth&lt;/td&gt;
&lt;td&gt;ISO 3166-2 subdivision codes and types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wikidata&lt;/td&gt;
&lt;td&gt;Missing subdivision IDs and capitals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CIA World Factbook&lt;/td&gt;
&lt;td&gt;Nationalities/demonyms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pipeline fetches source files daily, hashes them with SHA-256, and exits without writing anything when upstream data has not changed.&lt;/p&gt;

&lt;p&gt;When something changes, it builds five JSON files, validates them, pushes the public output into the API repo, and the API repo's seed workflow loads D1.&lt;/p&gt;

&lt;p&gt;That gives the Worker a boring job: read tables and return JSON.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhce7u06owiwnx0dwzw4r.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhce7u06owiwnx0dwzw4r.webp" alt="A notebook pipeline diagram showing source datasets flowing through hashing, validation, generation, database seeding, and deployment" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The best feature was removing features
&lt;/h2&gt;

&lt;p&gt;The first version had API key registration.&lt;/p&gt;

&lt;p&gt;Email form. Confirmation flow. Key storage. Per-key limits.&lt;/p&gt;

&lt;p&gt;Then I looked at it and asked: what am I protecting?&lt;/p&gt;

&lt;p&gt;The source data is public. The output is public. The project goal is adoption. Putting registration in front of a free country list only makes the product worse.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Removed&lt;/th&gt;
&lt;th&gt;What got better&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API keys&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;curl&lt;/code&gt; proves the API works&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Account creation&lt;/td&gt;
&lt;td&gt;No database table for users&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-key limits&lt;/td&gt;
&lt;td&gt;No identity layer just to throttle public data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email confirmation&lt;/td&gt;
&lt;td&gt;No delivery, spam, or bounce handling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are still platform limits and abuse protection at Cloudflare's layer. I just stopped building application-level gates for a dataset that was meant to be open.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this breaks down
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;City matching is name-based.&lt;/strong&gt; The location endpoint matches Cloudflare's city hint against D1 cities inside the detected country and state. Cities with identical names in the same state can collide.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;D1 has storage limits.&lt;/strong&gt; The current dataset fits comfortably. A future "every hamlet on earth" version would need a different storage/search plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The data is as good as its sources.&lt;/strong&gt; Cross-referencing helps, but upstream mistakes still make it through sometimes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The stack is Cloudflare-specific.&lt;/strong&gt; Self-hosters need Workers, D1, Wrangler, and the &lt;code&gt;request.cf&lt;/code&gt; object for the location endpoint.&lt;/p&gt;

&lt;p&gt;That is a deliberate trade-off. This project optimizes for a tiny operational footprint, not cloud portability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Start with the location endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.geocoded.me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then grab slim country data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.geocoded.me/countries?fields=name,iso2,emoji"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Search and drill down:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.geocoded.me/search?q=san+francisco&amp;amp;type=city"&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://api.geocoded.me/countries/IN/states?fields=name,iso2"&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://api.geocoded.me/countries/US/states/CA/cities?limit=10"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Timezones and currencies are separate resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.geocoded.me/timezones?fields=timezone,countryCodes&amp;amp;limit=5"&lt;/span&gt;
curl &lt;span class="s2"&gt;"https://api.geocoded.me/currencies?fields=code,name,symbol&amp;amp;limit=5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No API key. No signup. No demo account.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/harryy2510/geocoded" rel="noopener noreferrer"&gt;harryy2510/geocoded&lt;/a&gt; on GitHub&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What do you usually do for location data: pay for an API, ship a JSON package, self-host a dump, or hardcode the list and move on?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://harryy.me/blog/building-a-free-geolocation-api-on-cloudflare-workers" rel="noopener noreferrer"&gt;harryy.me&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>devops</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>Hello World</title>
      <dc:creator>Hariom Sharma</dc:creator>
      <pubDate>Fri, 24 Apr 2026 06:21:08 +0000</pubDate>
      <link>https://forem.com/harryy2510/hello-world-581f</link>
      <guid>https://forem.com/harryy2510/hello-world-581f</guid>
      <description>&lt;p&gt;I have been writing notes for years. Architecture decisions, debugging war stories, things I wish someone told me before I shipped that thing to production. All sitting in private docs nobody reads.&lt;/p&gt;

&lt;p&gt;That felt like a waste. So here we are.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4r7empxtbvo9v0alf8ww.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4r7empxtbvo9v0alf8ww.webp" alt="A notebook diagram showing private engineering notes becoming a public blog" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bother?
&lt;/h2&gt;

&lt;p&gt;I have shipped 15+ products across 10+ industries over the last decade. Google, Telefonica, startups you have never heard of. Every project taught me something. Most of those lessons live in my head or in scattered docs. A blog gives them a permanent home and forces me to think clearly about what I actually learned vs what I think I learned.&lt;/p&gt;

&lt;p&gt;A portfolio shows what I built. A blog shows how I think. If you are deciding whether to work with me, both matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to expect
&lt;/h2&gt;

&lt;p&gt;Short posts on real engineering problems. Architecture decisions where both options suck and you pick the less painful one. AI tooling: I have been deep in Claude Code, building plugins, automating my own workflow. Performance work. Shipping fast without shipping garbage.&lt;/p&gt;

&lt;p&gt;No listicles. No "ultimate guides." No sponsored content. Just notes from building things.&lt;/p&gt;

&lt;h2&gt;
  
  
  This blog's stack
&lt;/h2&gt;

&lt;p&gt;Astro with MDX on Cloudflare Workers. Zero client JavaScript (except a tiny search filter). Content Collections for type-safe frontmatter. Shiki for code highlighting. The whole thing scores 100 across Lighthouse.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;draft&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publishDate&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every post is an &lt;code&gt;.mdx&lt;/code&gt; file committed to the repo. Git is the CMS. No database, no build-time API calls, no dependencies that will break in six months.&lt;/p&gt;

&lt;p&gt;First real posts coming soon: starting with how I set up my AI development stack and why most people are burning tokens for nothing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://harryy.me/blog/hello-world" rel="noopener noreferrer"&gt;harryy.me&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>career</category>
      <category>programming</category>
      <category>meta</category>
    </item>
  </channel>
</rss>
