<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Abdur Rahman — Writing</title><description>Writing on AI, engineering, security, business and travel by Abdur Rahman.</description><link>https://abdur.tech/</link><language>en</language><atom:link href="https://abdur.tech/rss.xml" rel="self" type="application/rss+xml"/><item><title>You’re Using Claude. Here’s How to Make It More Useful in Your Daily Work.</title><link>https://abdur.tech/writing/making-claude-more-useful/</link><guid isPermaLink="true">https://abdur.tech/writing/making-claude-more-useful/</guid><description>You ask Claude to fix a bug. It makes a reasonable change.</description><pubDate>Sun, 25 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;You ask Claude to fix a bug. It makes a reasonable change.&lt;/p&gt;
&lt;p&gt;Then comes the follow-up:&lt;/p&gt;
&lt;p&gt;“Use pnpm.”&lt;/p&gt;
&lt;p&gt;“Don’t include those unrelated files.”&lt;/p&gt;
&lt;p&gt;“Check how we handle this elsewhere.”&lt;/p&gt;
&lt;p&gt;“Run the formatter.”&lt;/p&gt;
&lt;p&gt;Each instruction is small. Together, they leave you supervising details you’ve already explained.&lt;/p&gt;
&lt;p&gt;Claude has features that help with this, but the names can be confusing. Skills. Hooks. Subagents. MCP. Cowork. It takes a little work to understand where each one fits.&lt;/p&gt;
&lt;p&gt;The useful question is: &lt;strong&gt;what problem would I actually use this for?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let’s work through that using situations developers recognize: investigating a bug, committing changes, preparing release notes, and checking a spreadsheet against a CRM.&lt;/p&gt;
&lt;p&gt;The configurations are examples you can adapt. The sample findings and results are illustrative, so you know what to look for when trying them yourself.&lt;/p&gt;
&lt;h2 id=&quot;start-by-giving-claude-a-task-it-can-investigate&quot;&gt;Start by giving Claude a task it can investigate&lt;/h2&gt;
&lt;p&gt;Imagine a customer enquiry appears in a spreadsheet but never reaches your CRM.&lt;/p&gt;
&lt;p&gt;You could ask:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Fix the missing leads.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But “missing” leaves a lot open. Did the webhook arrive? Did the queue job run? Was a contact created without a lead? Did the lead enter another pipeline?&lt;/p&gt;
&lt;p&gt;A more useful request would be:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Investigate why submission TEST-002 did not appear in the CRM.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Trace the webhook handler, queue job, and CRM creation request.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Check whether it failed, was skipped, or created a lead elsewhere.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Show the relevant files and evidence before proposing changes.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;An existing contact does not necessarily mean a lead exists.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ve given Claude an identifier, an investigation path, and a business rule that affects the answer.&lt;/p&gt;
&lt;p&gt;For a substantial change, Claude Code’s Plan mode lets it investigate and propose an approach before implementation. Ask for the smallest proposed fix and how it would be verified.&lt;/p&gt;
&lt;p&gt;This also makes the response easier to assess. “The retry logic is broken” is a hypothesis. A specific condition in a specific handler gives you something to examine.&lt;/p&gt;
&lt;h2 id=&quot;the-project-facts-you-keep-repeating-belong-in-claudemd&quot;&gt;The project facts you keep repeating belong in &lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;You explain that the repository uses pnpm. Later, Claude suggests npm.&lt;/p&gt;
&lt;p&gt;You explain that controllers should stay thin. Another session puts business logic in a controller.&lt;/p&gt;
&lt;p&gt;Claude Code loads project guidance from &lt;code&gt;CLAUDE.md&lt;/code&gt;. You can run &lt;code&gt;/init&lt;/code&gt; to generate a starting version, then edit it to reflect how your team actually works.&lt;/p&gt;
&lt;p&gt;For a NestJS project, it might contain:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;# Working in this repository&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Use pnpm.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Keep controllers thin; business logic belongs in services.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Validate webhook payloads before enqueueing jobs.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Use source submission IDs for duplicate detection.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Database changes require migrations.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Run relevant checks and report their actual results.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now consider the next request:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Add duplicate protection to the lead-import handler.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude has a starting point for deciding where the logic belongs and which identifier matters.&lt;/p&gt;
&lt;p&gt;That doesn’t guarantee a correct implementation. It makes your expectations explicit and available across sessions.&lt;/p&gt;
&lt;p&gt;Keep this file focused on facts that change decisions. “Write clean code” gives little direction. “Use the source submission ID for duplicate detection” can prevent a particular mistake.&lt;/p&gt;
&lt;p&gt;Also, maintain it. An instruction describing an old queue system can send Claude in the wrong direction long after your code has changed.&lt;/p&gt;
&lt;h2 id=&quot;a-skill-saves-a-procedure-you-want-to-reuse&quot;&gt;A skill saves a procedure you want to reuse&lt;/h2&gt;
&lt;p&gt;Project conventions don’t fully describe how you want a particular task completed.&lt;/p&gt;
&lt;p&gt;Committing changes is a good example. You may want Claude to inspect the diff, exclude unrelated work, stage carefully, and write a message in your preferred format.&lt;/p&gt;
&lt;p&gt;Create:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;.claude/skills/commit/SKILL.md&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name: commit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;description: Review and commit changes belonging to the current task.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;disable-model-invocation: true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Inspect git status, unstaged changes, and staged changes.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Identify changes belonging to the current task.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Ask if unrelated changes cannot be separated confidently.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Stage only the intended files or hunks.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Review the staged diff before committing.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Write a concise Conventional Commit message explaining&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;the behavior changed.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Create the commit and report its hash and message.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Report which checks ran and which did not.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Do not push or amend unless requested.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then type:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;/commit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Suppose the working tree contains:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;M&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; src/leads/lead-import.service.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;M&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; src/leads/lead-import.service.spec.ts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;M&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; docs/pricing-notes.md&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The current task concerns importing leads. The pricing notes belong to something else.&lt;/p&gt;
&lt;p&gt;A useful result would identify that distinction, commit the relevant changes, and tell you what remains uncommitted. The message might be:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fix(leads): prevent duplicate imports for the same submission&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The frontmatter controls how the skill is discovered and invoked. The body describes the procedure.&lt;/p&gt;
&lt;p&gt;Here, &lt;code&gt;disable-model-invocation: true&lt;/code&gt; means you explicitly trigger the skill. Without that restriction, Claude can select relevant skills based on their descriptions. Invoking a skill does not bypass tool permissions.&lt;/p&gt;
&lt;p&gt;The benefit is consistency. You can review and improve the procedure once, then reuse it.&lt;/p&gt;
&lt;h2 id=&quot;skills-also-help-with-the-work-after-coding&quot;&gt;Skills also help with the work after coding&lt;/h2&gt;
&lt;p&gt;Think about release notes.&lt;/p&gt;
&lt;p&gt;A developer-facing change might say:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Add a unique constraint on source_submission_id and handle conflicts.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A customer-facing note could say:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Fixed an issue where repeated form submissions could create&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;duplicate leads.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That translation is another good skill:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name: release-notes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;description: Draft customer-facing release notes from a supplied diff or commit range.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Identify changes users will notice.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Group them under Added, Improved, and Fixed.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Explain observable behavior in plain language.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Exclude internal refactoring unless it affects users.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Do not invent benefits or claim unreleased changes are live.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Return a draft for review.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save it as &lt;code&gt;.claude/skills/release-notes/SKILL.md&lt;/code&gt;, then ask:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/release-notes Use the changes between v1.8.0 and v1.9.0.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The review still matters. If the draft claims “faster imports,” there should be evidence for that claim. A refactoring commit alone doesn’t establish a performance improvement.&lt;/p&gt;
&lt;p&gt;Build skills around procedures you genuinely repeat: API reviews, migration planning, incident summaries, or onboarding notes. A small collection of specific skills is easier to maintain than dozens you rarely use.&lt;/p&gt;
&lt;h2 id=&quot;a-hook-handles-a-step-you-want-to-happen-automatically&quot;&gt;A hook handles a step you want to happen automatically&lt;/h2&gt;
&lt;p&gt;Some tasks are mechanical enough to attach directly to an event.&lt;/p&gt;
&lt;p&gt;Formatting is one. You probably don’t want to remind Claude to run Prettier after every edit.&lt;/p&gt;
&lt;p&gt;Claude Code hooks can run commands at defined points in its workflow. &lt;code&gt;PreToolUse&lt;/code&gt; happens before a tool executes; &lt;code&gt;PostToolUse&lt;/code&gt; happens after a tool succeeds.&lt;/p&gt;
&lt;p&gt;For a project with Bash, &lt;code&gt;jq&lt;/code&gt;, and locally installed Prettier, create &lt;code&gt;.claude/hooks/format.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;set&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -euo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pipefail&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;edited_file&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;jq&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -er&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;.tool_input.file_path&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;./node_modules/.bin/prettier&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --write&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --ignore-unknown&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$edited_file&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Register it in &lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;hooks&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;PostToolUse&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;matcher&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Edit|Write&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;hooks&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;            &quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;            &quot;command&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;bash &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;$CLAUDE_PROJECT_DIR/.claude/hooks/format.sh&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;        ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Merge this with your existing settings.&lt;/p&gt;
&lt;p&gt;When Claude successfully uses &lt;code&gt;Edit&lt;/code&gt; or &lt;code&gt;Write&lt;/code&gt;, the hook receives the event data, extracts the file path, and runs Prettier on that file.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;/hooks&lt;/code&gt; to inspect the configuration. Then ask Claude to make a small edit in a disposable file and check whether the formatting follows your project configuration.&lt;/p&gt;
&lt;p&gt;This example has clear boundaries. It doesn’t catch files rewritten through Bash. It requires its dependencies to be installed. And because it runs afterward, it cannot prevent the original edit.&lt;/p&gt;
&lt;p&gt;For checks that should apply to every developer and every commit, keep Git hooks or CI involved too.&lt;/p&gt;
&lt;h2 id=&quot;a-smaller-hook-can-save-you-from-watching-the-terminal&quot;&gt;A smaller hook can save you from watching the terminal&lt;/h2&gt;
&lt;p&gt;You start an investigation, switch windows, and discover later that Claude has been waiting for input.&lt;/p&gt;
&lt;p&gt;On macOS, a &lt;code&gt;Notification&lt;/code&gt; hook can display a desktop notification. Add this event inside your existing &lt;code&gt;&quot;hooks&quot;&lt;/code&gt; object:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Notification&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;matcher&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;hooks&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;type&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;command&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;        &quot;command&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;osascript -e &apos;display notification &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Claude needs your attention&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; with title &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Claude Code&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This runs when Claude Code emits a matching notification event. macOS notification permissions must allow the notification to appear.&lt;/p&gt;
&lt;p&gt;It’s a modest improvement, but it makes longer tasks easier to leave running while you work elsewhere.&lt;/p&gt;
&lt;h2 id=&quot;a-subagent-can-investigate-one-question-without-overwhelming-the-main-conversation&quot;&gt;A subagent can investigate one question without overwhelming the main conversation&lt;/h2&gt;
&lt;p&gt;Back to duplicate leads.&lt;/p&gt;
&lt;p&gt;The investigation might involve a webhook handler, a queue worker, a CRM client, and database constraints. You need the findings, but you don’t necessarily need every intermediate file read in the main conversation.&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;.claude/agents/lead-investigator.md&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;name: lead-investigator&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;description: Investigates missing or duplicate leads across webhook, queue, database, and CRM code.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;tools: Read, Grep, Glob&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;model: inherit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Investigate the supplied issue without changing files.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Trace the relevant processing flow.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Separate confirmed evidence from hypotheses.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Cite files and functions supporting each finding.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Return:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Likely cause&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Evidence&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Unresolved questions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- A suggested way to verify the finding&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then ask:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Use lead-investigator to check whether two workers could&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;create leads for the same submission.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A hypothetical report might say:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Finding:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Both workers can pass the duplicate lookup before either&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;records the submission as processed.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Evidence:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;The lookup and insert are separate operations, and the schema&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;has no unique constraint on the submission identifier.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Verification:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Process two jobs concurrently with the same submission ID.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main Claude session now has a specific hypothesis to examine.&lt;/p&gt;
&lt;p&gt;You can ask it to check the evidence and propose a fix. A local database constraint might protect local records, while preventing duplicate remote CRM requests also requires attention to retries and when external IDs are stored.&lt;/p&gt;
&lt;p&gt;A custom subagent has its own context and configurable tools. The main session must pass the details needed for the task. In this example, the tool list limits the worker to reading and searching.&lt;/p&gt;
&lt;p&gt;Subagents consume usage and add overhead. They’re useful for substantial, focused investigations. A one-line change rarely needs several workers.&lt;/p&gt;
&lt;p&gt;Separate context also doesn’t automatically mean separate files. Agents with editing tools may affect the same checkout unless you arrange filesystem isolation.&lt;/p&gt;
&lt;h2 id=&quot;mcp-gives-claude-a-way-to-reach-your-actual-tools&quot;&gt;MCP gives Claude a way to reach your actual tools&lt;/h2&gt;
&lt;p&gt;Reading the repository shows what the application intends to do. Sometimes you need to inspect what actually happened in the CRM.&lt;/p&gt;
&lt;p&gt;MCP stands for Model Context Protocol. It provides a standard interface for exposing tools and data to AI applications. A connector can make operations backed by your own services available to Claude.&lt;/p&gt;
&lt;p&gt;For example, you could implement:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;findLeadBySubmissionId(submissionId)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;getLeadNotes(leadId)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;createLead(input)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These are illustrative custom tools, not built-in Kommo commands.&lt;/p&gt;
&lt;p&gt;A tool request could look like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;tool&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;findLeadBySubmissionId&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;arguments&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;    &quot;submissionId&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;TEST-002&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your server responds:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;matches&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: [],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;searchComplete&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude can use that result to propose creating the missing lead.&lt;/p&gt;
&lt;p&gt;But consider a different response:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;error&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;CRM request timed out&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  &quot;searchComplete&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is not evidence that the lead is missing.&lt;/p&gt;
&lt;p&gt;This is why tool design matters. An empty result and a failed search need different representations. Otherwise, a temporary API failure could become a duplicate record.&lt;/p&gt;
&lt;p&gt;For an API integration, your server stores the credentials. Claude requests an operation; your backend validates the request, checks authorization, calls the provider, and returns the result.&lt;/p&gt;
&lt;h2 id=&quot;cowork-can-handle-the-operational-side-of-the-same-problem&quot;&gt;Cowork can handle the operational side of the same problem&lt;/h2&gt;
&lt;p&gt;Claude Code helps you investigate and change the integration.&lt;/p&gt;
&lt;p&gt;Cowork brings agent capabilities to work involving documents, spreadsheets, connected applications, and browser tasks. What it can do depends on the tools and access available in your setup.&lt;/p&gt;
&lt;p&gt;Our project has an operational problem as well as an engineering problem.&lt;/p&gt;
&lt;p&gt;The engineer needs to fix duplicate handling. The operations team needs to identify which enquiries were missed.&lt;/p&gt;
&lt;p&gt;Try a small experiment with fictional data:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Submission&lt;/th&gt;&lt;th&gt;In the sheet?&lt;/th&gt;&lt;th&gt;In the CRM?&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;TEST-001&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TEST-002&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TEST-003&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Give Claude this assignment:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Compare these three submissions with the CRM.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Match by submission ID and include matching lead links.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;An existing contact alone does not prove a lead exists.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Report Existing, Missing, or Uncertain.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Prepare the proposed additions for review.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The expected result is one existing lead and two missing leads.&lt;/p&gt;
&lt;p&gt;After checking the comparison, authorize the two creations and ask Claude to record their URLs in the sheet. Then repeat the comparison.&lt;/p&gt;
&lt;p&gt;The second run should find three existing leads and create nothing.&lt;/p&gt;
&lt;p&gt;That repeat run tells you something valuable. A workflow that succeeds once but duplicates records on retry needs more work.&lt;/p&gt;
&lt;p&gt;For browser-based experiments, start small enough that you can inspect every action and result.&lt;/p&gt;
&lt;h2 id=&quot;having-access-and-having-approval-are-different-things&quot;&gt;Having access and having approval are different things&lt;/h2&gt;
&lt;p&gt;A CRM token may allow your application to create thousands of records. Your task may authorize creating two.&lt;/p&gt;
&lt;p&gt;Your application should enforce that smaller scope.&lt;/p&gt;
&lt;p&gt;For custom tools, check the authenticated customer, permitted account, allowed pipeline, and approved actions in backend code.&lt;/p&gt;
&lt;p&gt;A tool call containing &lt;code&gt;approved: true&lt;/code&gt; is not proof that a human approved anything. That decision needs to come from trusted application state.&lt;/p&gt;
&lt;p&gt;Similarly, “never delete records” in a prompt is useful guidance, but removing deletion capability provides a stronger restriction.&lt;/p&gt;
&lt;p&gt;Claude Code’s Auto mode concerns permission handling for tool actions. It doesn’t establish that Claude understood the requirements or that a code change is correct.&lt;/p&gt;
&lt;p&gt;Review the result with the same questions you would ask another developer: what changed, why, and what evidence supports it?&lt;/p&gt;
&lt;h2 id=&quot;sometimes-a-scheduled-job-is-still-the-right-answer&quot;&gt;Sometimes a scheduled job is still the right answer&lt;/h2&gt;
&lt;p&gt;While learning about agents, I kept coming back to this question:&lt;/p&gt;
&lt;p&gt;“I can already build this with NestJS. Why would I need an agent?”&lt;/p&gt;
&lt;p&gt;Suppose the rule is to read new rows every morning and create missing CRM records. A scheduled backend job may be the simplest implementation.&lt;/p&gt;
&lt;p&gt;The sequence is known. Retries, duplicate protection, and failure handling can be explicit.&lt;/p&gt;
&lt;p&gt;An agent becomes useful when the next step depends on interpreting what it discovers.&lt;/p&gt;
&lt;p&gt;One customer asks about delivery, so it retrieves the delivery policy. Another asks to be contacted next month, so it proposes a reminder. A third already received an answer, so it skips another follow-up.&lt;/p&gt;
&lt;p&gt;The sequence changes because the information changes.&lt;/p&gt;
&lt;p&gt;You can also combine ordinary software with a bounded AI step. Let code control eligibility, scheduling, and sending while a model drafts the message. That can be useful without handing the entire process to an agent.&lt;/p&gt;
&lt;h2 id=&quot;start-with-one-instruction-youre-tired-of-repeating&quot;&gt;Start with one instruction you’re tired of repeating&lt;/h2&gt;
&lt;p&gt;You don’t need to configure every feature before your next task.&lt;/p&gt;
&lt;p&gt;Maybe you keep explaining how commits should be written. Maybe you keep asking Claude to run the formatter. Maybe a long investigation leaves the main conversation full of details you no longer need.&lt;/p&gt;
&lt;p&gt;Give that problem a home: project instructions, a skill, a hook, or a focused subagent. Then see whether you need to intervene less often without losing confidence in the result.&lt;/p&gt;
&lt;p&gt;The next time you catch yourself typing “I already told you this,” you have somewhere useful to put that instruction.&lt;/p&gt;</content:encoded><category>AI</category><category>ai</category><category>claude</category><category>tooling</category><author>riyad.abdur@gmail.com (Abdur Rahman)</author></item><item><title>Why the Best Manufacturer in the Category Is Invisible on Google</title><link>https://abdur.tech/writing/best-manufacturer-invisible-on-google/</link><guid isPermaLink="true">https://abdur.tech/writing/best-manufacturer-invisible-on-google/</guid><description>A manufacturer with a twenty-year reputation asked me why two brands nobody had heard of were beating them in search. It wasn’t a trick.</description><pubDate>Sat, 24 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;A manufacturer with a twenty-year reputation asked me why two brands nobody had heard of were beating them in search. It wasn’t a trick. It was a sentence they had never written.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;This kind of email turns up more often than you would think.&lt;/p&gt;
&lt;p&gt;This one came from the owner of a company that makes industrial storage systems. Heavy-duty racking and mezzanines, the kind of steel you bolt into a warehouse floor and then forget about for a decade. They had been in business for twenty years, held every certification that mattered in their market, and had customers who had been with them since the start.&lt;/p&gt;
&lt;p&gt;He had typed a question into Google that any of his buyers might type: &lt;em&gt;average cost of a pallet racking system for a 10,000 square foot warehouse.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The results were full of two brands he had barely heard of, young companies three or four years old that import most of their product. His sales team had started hearing their names on calls. “They are not serious players,” he wrote. “So what are they doing to skew the results? Whatever it is, it’s working. We need to study that and copy it.”&lt;/p&gt;
&lt;p&gt;It is a familiar question. The assumption is that there is a trick. There almost never is. What there usually is, is a sentence that one company wrote down and the other company didn’t.&lt;/p&gt;
&lt;p&gt;This is a story about finding that sentence, and about why the companies with the most to say are so often the ones saying nothing.&lt;/p&gt;
&lt;h2 id=&quot;read-the-result-page-like-a-document&quot;&gt;Read the result page like a document&lt;/h2&gt;
&lt;p&gt;I ran his query and read the whole result page, top to bottom, instead of skimming the first few links.&lt;/p&gt;
&lt;p&gt;The first thing on the page was not a website. It was a row of product cards with photos and prices. A 10-bay starter system at $4,200, a 20-bay package at $7,900, a heavy-duty run at $12,500. Five sellers and five prices before a single blue link.&lt;/p&gt;
&lt;p&gt;Then came the articles. A regional installer suggesting a budget of $15 to $40 per pallet position. A trade magazine piece from three years ago. A marketplace listing. And then, from one of the two upstarts, a guide published in 2022 with a sentence that stopped me.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For a standard warehouse, plan on roughly $8 to $12 per square foot for selective racking, installed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There was the mystery, solved in one line. A four-year-old sentence with a dollar sign in it, sitting on a page with a question for a headline, had become Google’s answer to “what does this cost.” Every time a buyer asked, the upstart answered, and it would keep answering until someone wrote a better sentence.&lt;/p&gt;
&lt;p&gt;I went back to my client’s website and searched it for the word “price,” then “cost,” then the dollar sign. Nothing. Thirty pages, a detailed FAQ, downloadable load tables, a warranty far better than the competition’s, and not one number a buyer could anchor to.&lt;/p&gt;
&lt;p&gt;The search engine wasn’t skewed. It was answering the question that was asked. My client had never entered the conversation.&lt;/p&gt;
&lt;h2 id=&quot;a-good-company-described-badly&quot;&gt;A good company, described badly&lt;/h2&gt;
&lt;p&gt;A closer look at my client’s own site confirmed something that is common with manufacturers. The business was far better than the website said it was.&lt;/p&gt;
&lt;p&gt;The Google Business Profile had dozens of five-star reviews. Customers wrote about engineers who drove out to re-measure a site for free. The steel was rolled domestically, the powder coat was applied in-house, and the design team held seismic certifications the imports could not match. This is a company with a story worth telling.&lt;/p&gt;
&lt;p&gt;The website told almost none of it in a way a search engine could read. A few examples.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It never used the buyer’s words.&lt;/strong&gt; Every page said “storage solutions.” Buyers type “pallet racking,” “warehouse shelving,” “mezzanine floor.” The phrase “pallet racking” appeared on two pages out of thirty. Google is good at synonyms and still prefers the page that uses the words people type.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It named its products after itself.&lt;/strong&gt; The systems were called things like “Series 400” and “ProLine HD.” Fine names for a catalog. Almost nobody searches for them. Every page title was a model name followed by the company name, which is the digital equivalent of a shop sign that says only the owner’s surname.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It told the machines the wrong address.&lt;/strong&gt; Buried in the page code was a structured-data block, the kind search engines and AI assistants read to understand who a company is. It listed the headquarters correctly, then declared the country as somewhere the company had once had a distributor. The phone number in that block did not match the one in the footer. A sister site for another market claimed the same address with no link back. From a machine’s point of view, there were two companies, one of them possibly foreign.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Every page had two headlines.&lt;/strong&gt; A newsletter box in the footer had been built as a top-level heading, so every page on the site announced two subjects, one of which was “Join our mailing list.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The best content was hidden.&lt;/strong&gt; The FAQ answered forty real questions about load ratings, floor requirements, lead times and installation. Real expertise, clearly written by people who had answered these questions on the phone a thousand times. None of it was marked up so a search engine could use it, none of it appeared on the product pages, and none of it touched price.&lt;/p&gt;
&lt;p&gt;None of these are hard to fix, which is what made the email hard to answer. The client assumed he was losing to a sophisticated opponent. He was mostly losing to a template setting and a missing page.&lt;/p&gt;
&lt;h2 id=&quot;what-the-upstarts-were-actually-doing&quot;&gt;What the upstarts were actually doing&lt;/h2&gt;
&lt;p&gt;I spent some time on the two competitors’ sites, looking for the trick my client was sure existed.&lt;/p&gt;
&lt;p&gt;I didn’t find one. Neither site had structured data worth mentioning. One had a copy-paste error on its main product page that referred to a completely different product. Their content was thin. Their backlinks, as far as I could tell, were ordinary.&lt;/p&gt;
&lt;p&gt;What they had was three habits.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;They wrote the number down.&lt;/strong&gt; One had published four separate cost guides, each with a table that gave a figure for a small warehouse, a mid-size one, and installation on top. Every section heading was a question a buyer would type. The newest one had the year in its title and a date two weeks old. The other brand put a “From $X” on every product listing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;They put the products where the shopping results look.&lt;/strong&gt; Both had product feeds in Google Merchant Center, which is why their systems appeared as priced cards above the organic results. One also sold through a big-box marketplace, and the marketplace listings carried exact dimensions in the title. My client’s buyers who “kept mentioning” that brand were seeing it on a marketplace shelf, not Google’s.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;They claimed geography.&lt;/strong&gt; One brand had a page for every state it shipped to. When I searched for my client’s own company name plus his city, that competitor’s state page ranked above him. Above his own name. Not through any manipulation. My client simply had no page about his home state at all, despite having a factory there.&lt;/p&gt;
&lt;p&gt;That was the whole secret. Publish prices, feed the shopping results, say where you are. The two brands weren’t gaming the system. They were the only ones in the category doing the obvious things, and the obvious things add up.&lt;/p&gt;
&lt;h2 id=&quot;five-decisions-not-a-checklist&quot;&gt;Five decisions, not a checklist&lt;/h2&gt;
&lt;p&gt;The full audit ran to about forty items. But the conversation that mattered was about five decisions, and each one was uncomfortable in its own way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Publish a price range, and accept that it will be wrong for someone.&lt;/strong&gt; Custom manufacturers hate this. Every project is different, and a range feels like a promise. But a buyer who sees “$X to $Y for a typical 10,000 square foot layout” and then gets a quote inside that range trusts you more, not less. The buyer who never sees a number goes to the competitor who printed one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Put standard configurations in the shopping results.&lt;/strong&gt; The company already stocked common packages. Free listings in Merchant Center cost nothing beyond the setup time. A handful of products with real prices, photographed well, is enough to get the brand into a carousel that until now belonged entirely to competitors. Of the five, this is the quickest to act on and needs the least writing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Rename the pages for the buyer, not the catalog.&lt;/strong&gt; “Series 400” became “Heavy-Duty Selective Pallet Racking.” The model number stayed in the subtitle. Nothing about the product changed, only who could find it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Tell the machines who you are, once, consistently.&lt;/strong&gt; One company name, one address, one phone number, and one account of where the steel is rolled and the coating is applied. Write it on an About page (the site didn’t have one), repeat it in structured data that names the right country, and link the sister site properly so Google can tell it is the same company speaking another language rather than a rival.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Claim the ground you already stand on.&lt;/strong&gt; A page for the home state and the factory. Pages for the seismic certifications that matter in certain regions. The company had earned each of these. It had just never said so anywhere a search engine would look.&lt;/p&gt;
&lt;p&gt;None of this requires a bigger budget. It requires deciding that the website is a salesperson who should answer the question it was asked.&lt;/p&gt;
&lt;h2 id=&quot;the-gap-in-one-picture&quot;&gt;The gap in one picture&lt;/h2&gt;
&lt;p&gt;[Image: Infographic comparing an established manufacturer with no prices online against two young brands with priced cards, cost guides and state pages]&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Side by side: an established manufacturer with no price on its site, and two young brands with priced product cards, dated cost guides and state pages. The established company has more to say and says less of it where search engines look.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-takeaway&quot;&gt;The takeaway&lt;/h2&gt;
&lt;p&gt;Search engines and AI assistants can’t test a product. They go with the clearest answer they can find. The competitors here didn’t outspend or outsmart anyone. They just answered the question.&lt;/p&gt;
&lt;p&gt;The established company doesn’t need to copy them. It has more to say than they do. It only needs to say it where buyers are looking.&lt;/p&gt;</content:encoded><category>Business</category><category>seo</category><category>business</category><author>riyad.abdur@gmail.com (Abdur Rahman)</author></item><item><title>Why Lumen Failed: The Rise and Fall of Laravel’s Micro-Framework</title><link>https://abdur.tech/writing/why-lumen-failed/</link><guid isPermaLink="true">https://abdur.tech/writing/why-lumen-failed/</guid><description>In 2015, Laravel introduced Lumen, a microframework designed for blazing-fast APIs and microservices. It promised a lighter, speedier alternative to Laravel, ideal for stateless services.</description><pubDate>Sun, 20 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;[Image: Timeline of the rise and fall of Laravel Lumen from its 2015 launch to Laravel absorbing its use cases in 2023]&lt;/p&gt;
&lt;p&gt;In 2015, Laravel introduced &lt;strong&gt;Lumen&lt;/strong&gt;, a microframework designed for blazing-fast APIs and microservices. It promised a lighter, speedier alternative to Laravel, ideal for stateless services. However, a decade later, Lumen has largely faded from the spotlight. Even Laravel’s creator, Taylor Otwell, no longer recommends it, favoring Laravel Octane instead.&lt;/p&gt;
&lt;p&gt;So, what led to Lumen’s decline? Let’s delve into the key factors behind its downfall and explore similar cases in the tech world.&lt;/p&gt;
&lt;h2 id=&quot;1-the-promise-of-lumen-speed-and-simplicity&quot;&gt;1. The Promise of Lumen: Speed and Simplicity&lt;/h2&gt;
&lt;p&gt;Lumen was marketed as a faster, leaner version of Laravel, designed specifically for microservices and high-performance APIs. Key selling points included:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Blazing Fast Performance&lt;/strong&gt;: Benchmarks showed Lumen was outperforming Laravel in raw speed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Minimalist Architecture&lt;/strong&gt;: Stripped-down features like no sessions, cookies, or Blade templating made it ideal for stateless APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy Upgrade Path&lt;/strong&gt;: Since Lumen shared Laravel’s core components, developers could transition to Laravel if their project outgrew Lumen.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a while, companies like &lt;strong&gt;Trivago, Uber, and ZipRecruiter&lt;/strong&gt; used Lumen for backend APIs, leveraging its efficiency for high-throughput services.&lt;/p&gt;
&lt;h2 id=&quot;why-lumen-ultimately-failed&quot;&gt;Why Lumen Ultimately Failed&lt;/h2&gt;
&lt;p&gt;Despite its early promise, Lumen struggled to justify its existence as a separate framework. Here’s why:&lt;/p&gt;
&lt;h3 id=&quot;a-laravels-performance-improvements&quot;&gt;A. Laravel’s Performance Improvements&lt;/h3&gt;
&lt;p&gt;Over time, Laravel optimized its core, reducing the performance gap. Features like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Route caching&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OPcache integration&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improved middleware handling&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Together, these made Laravel nearly as fast as Lumen for most API use cases.&lt;/p&gt;
&lt;h3 id=&quot;b-feature-limitations-outweighed-speed-gains&quot;&gt;B. Feature Limitations Outweighed Speed Gains&lt;/h3&gt;
&lt;p&gt;Lumen lacked critical Laravel features, forcing developers to reintegrate them manually:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No built-in authentication&lt;/strong&gt; (e.g., Sanctum/Passport)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limited database &amp;#x26; queue support&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No Blade templating or frontend tooling&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This meant that as projects grew, developers often had to either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Patch Lumen with Laravel components&lt;/strong&gt; (defeating the purpose of a “lightweight” framework).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Migrate to Laravel entirely&lt;/strong&gt; (which many did).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;c-maintenance-overhead-for-the-laravel-team&quot;&gt;C. Maintenance Overhead for the Laravel Team&lt;/h3&gt;
&lt;p&gt;Maintaining two separate frameworks (Laravel &amp;#x26; Lumen) became redundant. By 2021, Taylor Otwell (Laravel’s creator) announced that &lt;strong&gt;Lumen would no longer be actively maintained&lt;/strong&gt;, recommending Laravel for APIs instead.&lt;/p&gt;
&lt;h3 id=&quot;d-the-rise-of-serverless--api-first-laravel&quot;&gt;D. The Rise of Serverless &amp;#x26; API-First Laravel&lt;/h3&gt;
&lt;p&gt;With tools like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Laravel Sanctum&lt;/strong&gt; (for API auth)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Laravel Octane&lt;/strong&gt; (for performance boosts)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improved API resource handling&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Laravel itself became a better choice for APIs, making Lumen obsolete.&lt;/p&gt;
&lt;h2 id=&quot;when-did-lumen-make-sense-and-when-did-it-not&quot;&gt;When Did Lumen Make Sense? (And When Did It Not?)&lt;/h2&gt;
&lt;h3 id=&quot;good-for&quot;&gt;Good for&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Ultra-low-latency microservices (where every millisecond counts).&lt;/li&gt;
&lt;li&gt;Legacy projects already on Lumen.&lt;/li&gt;
&lt;li&gt;Tiny APIs with no need for Laravel’s full feature set.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;bad-for&quot;&gt;Bad for&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Apps requiring robust authentication (e.g., OAuth2).&lt;/li&gt;
&lt;li&gt;Teams that later realized they needed Laravel’s features.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-final-nail-in-the-coffin-laravels-dominance&quot;&gt;The Final Nail in the Coffin: Laravel’s Dominance&lt;/h2&gt;
&lt;p&gt;By 2023, Laravel had:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Matched Lumen’s speed&lt;/strong&gt; in real-world API scenarios.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Integrated better API tooling&lt;/strong&gt; (e.g., API resources, rate limiting).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Made Lumen redundant&lt;/strong&gt; for most use cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even benchmarks showed that &lt;strong&gt;in real-world apps (with DB queries, caching, etc.), the performance difference was negligible&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;conclusion-why-lumen-failed&quot;&gt;Conclusion: Why Lumen Failed&lt;/h2&gt;
&lt;p&gt;Lumen was a good idea in theory but failed because:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Laravel caught up in speed&lt;/strong&gt; and API capabilities.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feature limitations made it impractical&lt;/strong&gt; for growing projects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintaining two frameworks was inefficient&lt;/strong&gt; for the Laravel ecosystem.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Today, &lt;strong&gt;Laravel is the better choice for APIs&lt;/strong&gt;, while Lumen remains a relic of an era when micro-frameworks seemed essential. For most developers, &lt;strong&gt;Laravel + Octane or plain Laravel APIs&lt;/strong&gt; are the way forward.&lt;/p&gt;
&lt;h2 id=&quot;final-thought&quot;&gt;Final Thought&lt;/h2&gt;
&lt;p&gt;Lumen’s demise teaches us that &lt;strong&gt;optimizing for raw speed alone isn’t enough&lt;/strong&gt; — flexibility, maintainability, and ecosystem support matter just as much.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bacancytechnology.com/blog/laravel-vs-lumen&quot;&gt;Why Lumen Was Deprecated&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.esparkinfo.com/software-development/technologies/laravel/laravel-vs-lumen&quot;&gt;Lumen’s Limitations in Real-World Apps&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Engineering</category><category>laravel</category><category>php</category><author>riyad.abdur@gmail.com (Abdur Rahman)</author></item><item><title>Unlock Free AI-Powered Coding Assistance with DeepSeek-Coder-V2 and Ollama: Your Local, Lightweight Code Companion</title><link>https://abdur.tech/writing/deepseek-coder-v2-with-ollama/</link><guid isPermaLink="true">https://abdur.tech/writing/deepseek-coder-v2-with-ollama/</guid><description>In today’s fast-paced development world, AI tools are transforming how we write code.</description><pubDate>Wed, 19 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In today’s fast-paced development world, AI tools are transforming how we write code. DeepSeek-Coder-V2, a nice open-source LLM (Large Language Model), paired with Ollama, offers an exceptional local coding assistant — all for free. With Ollama, you can run DeepSeek-Coder-V2 efficiently on your machine, eliminating the need for cloud APIs.&lt;/p&gt;
&lt;p&gt;This guide walks you through the process of installing and using DeepSeek-Coder-V2 with Ollama, integrating it with VS Code’s &lt;strong&gt;CodeGPT&lt;/strong&gt; extension for AI-driven autocompletion, and comparing its performance with other models available in Ollama.&lt;/p&gt;
&lt;h2 id=&quot;why-deepseek-coder-v2-with-ollama-is-a-game-changer-for-developers&quot;&gt;Why DeepSeek-Coder-V2 with Ollama is a Game-Changer for Developers&lt;/h2&gt;
&lt;p&gt;Here’s why this setup should be on your radar:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Free &amp;#x26; Open Source&lt;/strong&gt; — No subscription needed. Run it locally without incurring cloud fees.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Optimized for Code&lt;/strong&gt; — Trained on a wide range of programming languages, DeepSeek-Coder-V2 is a solid alternative to proprietary models.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lightweight &amp;#x26; Efficient&lt;/strong&gt; — Despite having 236B parameters in its original form, the Ollama-optimized version is just ~8.9GB, meaning it can run smoothly on most modern machines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Seamless Integration with VS Code&lt;/strong&gt; — Unlock the power of AI-assisted code autocompletions with the &lt;strong&gt;CodeGPT VS Code extension&lt;/strong&gt;, boosting your productivity.&lt;/p&gt;
&lt;h2 id=&quot;how-to-install-and-run-deepseek-coder-v2-with-ollama&quot;&gt;How to Install and Run DeepSeek-Coder-V2 with Ollama&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Install Ollama&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ollama is an intuitive tool for running language models locally. To get started:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS and Linux:&lt;/strong&gt; run the following command in your terminal:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;curl&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -fsSL&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://ollama.com/install.sh&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Windows:&lt;/strong&gt; download and install Ollama from &lt;a href=&quot;https://ollama.com/&quot;&gt;ollama.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Download DeepSeek-Coder-V2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Once Ollama is installed, pull the DeepSeek-Coder-V2 model:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ollama&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; pull&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; deepseek-coder-v2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The quantized version is only ~8.9GB, much smaller than its original form.&lt;/p&gt;
&lt;p&gt;To test if it’s working, run:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;ollama&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; deepseek-coder-v2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will start an interactive session where you can ask coding-related queries.&lt;/p&gt;
&lt;h2 id=&quot;integrating-deepseek-coder-v2-with-vs-code-and-codegpt&quot;&gt;Integrating DeepSeek-Coder-V2 with VS Code and CodeGPT&lt;/h2&gt;
&lt;p&gt;VS Code, combined with &lt;strong&gt;CodeGPT&lt;/strong&gt;, provides smooth AI-powered code autocompletion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 1: Install the CodeGPT Extension&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;strong&gt;VS Code&lt;/strong&gt; and go to the &lt;strong&gt;Extensions&lt;/strong&gt; panel (Ctrl + Shift + X).&lt;/li&gt;
&lt;li&gt;Search for &lt;strong&gt;CodeGPT&lt;/strong&gt; and install it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;[Image: VS Code Extensions Marketplace search for CodeGPT, with the CodeGPT: Chat &amp;#x26; AI Agents extension highlighted]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 2: Configure CodeGPT to Use Ollama&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open the CodeGPT menu and select the autocomplete setting.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;[Image: CodeGPT Chat panel in VS Code with the autocomplete settings icon highlighted in the toolbar]&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Select the deepseek-coder-v2 model, save the settings and restart VS Code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;[Image: CodeGPT AI code auto-completion dialog with deepseek-coder-v2 (Ollama) selected from the model list]&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Step 3: Try AI Autocompletion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Open any code file, start typing a function, and see how DeepSeek-Coder-V2 suggests autocompletions based on the context.&lt;/p&gt;
&lt;p&gt;For example, if you write a comment like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;// write a program in Go that prints &quot;Hello, World!&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will generate:&lt;/p&gt;
&lt;p&gt;[Image: Editor showing a Go Hello, World program generated from a one-line comment prompt]&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Running DeepSeek-Coder-V2 locally via Ollama is a great way to get AI-assisted coding — free, efficient, and easy to use. Pairing it with VS Code’s &lt;strong&gt;CodeGPT&lt;/strong&gt; extension brings even more productivity boosts, all without the hassle of cloud costs.&lt;/p&gt;
&lt;p&gt;If you’re searching for a free and powerful alternative to GitHub Copilot, give this setup a try.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Next Steps&lt;/strong&gt;: Test it yourself and share your experience!&lt;/p&gt;</content:encoded><category>AI</category><category>ai</category><category>llm</category><category>tooling</category><author>riyad.abdur@gmail.com (Abdur Rahman)</author></item><item><title>3 Essential Tips for Secure JWT Management and Preventing Token Theft</title><link>https://abdur.tech/writing/secure-jwt-management/</link><guid isPermaLink="true">https://abdur.tech/writing/secure-jwt-management/</guid><description>JWTs are widely used for authentication, but securing them requires careful handling to avoid vulnerabilities and potential threats.</description><pubDate>Fri, 29 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;[Image: Pyramid diagram of JWT security layers: secure storage at the base, short lifespan, and regular rotation at the top]&lt;/p&gt;
&lt;p&gt;JSON Web Tokens (JWTs) are a popular way to manage user authentication in web applications. However, handling JWTs securely requires a thoughtful approach to mitigate common threats like Cross-Site Scripting (XSS), Cross-Site Request Forgery (CSRF), and token theft. Here are three key strategies for securely storing and managing JWTs.&lt;/p&gt;
&lt;h2 id=&quot;1-prioritize-cookies-over-local-storage&quot;&gt;1. Prioritize Cookies Over Local Storage&lt;/h2&gt;
&lt;p&gt;When it comes to storing JWTs on the frontend, many developers opt for &lt;code&gt;localStorage&lt;/code&gt; or &lt;code&gt;sessionStorage&lt;/code&gt; due to their simplicity. However, these storage mechanisms are vulnerable to &lt;strong&gt;XSS&lt;/strong&gt; attacks. If an attacker manages to inject JavaScript into your application, they could easily access sensitive data stored in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;why-cookies-are-better&quot;&gt;Why Cookies Are Better&lt;/h3&gt;
&lt;p&gt;Cookies, on the other hand, can be configured with security features that significantly reduce this risk:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;HttpOnly&lt;/code&gt; flag&lt;/strong&gt;: By marking the cookie as &lt;code&gt;HttpOnly&lt;/code&gt;, you prevent JavaScript from accessing it, thus mitigating the risk of XSS attacks. Even if an attacker gains access to the frontend code, they won’t be able to steal the token.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Secure&lt;/code&gt; flag&lt;/strong&gt;: The &lt;code&gt;Secure&lt;/code&gt; flag ensures that cookies are only transmitted over HTTPS connections. This prevents man-in-the-middle (MITM) attacks where the token could be intercepted over an insecure HTTP connection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SameSite=Strict&lt;/code&gt;&lt;/strong&gt;: This attribute ensures that the cookie is only sent with same-origin requests, limiting the risk of &lt;strong&gt;CSRF&lt;/strong&gt; attacks. CSRF can occur when an attacker tricks a user’s browser into sending a request to your site with the user’s credentials or token, potentially executing malicious actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By using cookies with these flags, you can safely store JWTs and prevent common security vulnerabilities.&lt;/p&gt;
&lt;h2 id=&quot;2-implement-token-rotation-with-short-lived-access-tokens&quot;&gt;2. Implement Token Rotation with Short-Lived Access Tokens&lt;/h2&gt;
&lt;p&gt;One of the main security concerns with JWTs is that if a token is compromised, it could be used indefinitely. To reduce this risk, it’s crucial to implement &lt;strong&gt;token rotation&lt;/strong&gt; with short-lived access tokens.&lt;/p&gt;
&lt;h3 id=&quot;how-token-rotation-works&quot;&gt;How Token Rotation Works&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Access Tokens&lt;/strong&gt;: Set access tokens to expire quickly (e.g., 15 minutes or 1 hour). Since these tokens provide access to protected resources, their expiration limits the time an attacker can use a stolen token.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refresh Tokens&lt;/strong&gt;: Use refresh tokens to allow users to obtain new access tokens without reauthenticating. However, refresh tokens should be securely stored in &lt;code&gt;HttpOnly&lt;/code&gt; cookies to prevent JavaScript access and mitigate the risk of XSS attacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rotation Mechanism&lt;/strong&gt;: Implement a system where, each time an access token is refreshed, a new access token and refresh token are issued. Additionally, invalidate the old refresh token immediately to prevent replay attacks (where an attacker tries to reuse a stolen refresh token).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-token-rotation-enhances-security&quot;&gt;Why Token Rotation Enhances Security&lt;/h3&gt;
&lt;p&gt;Token rotation significantly reduces the impact of token theft. Even if an attacker manages to steal an access token, they will only have a short window to use it. The refresh token, securely stored in cookies, can only be used to obtain a new access token when it’s valid, and rotating refresh tokens ensures that stale or stolen tokens won’t be accepted.&lt;/p&gt;
&lt;h2 id=&quot;3-split-large-jwts-across-multiple-cookies&quot;&gt;3. Split Large JWTs Across Multiple Cookies&lt;/h2&gt;
&lt;p&gt;JWTs are typically small and concise, but in some cases, they can grow large if they contain extensive claims, permissions, or user data. Browsers impose a size limit on cookies (usually around 4KB), which means that storing a large JWT in a single cookie could exceed this limit and result in errors.&lt;/p&gt;
&lt;h3 id=&quot;solution-for-large-tokens&quot;&gt;Solution for Large Tokens&lt;/h3&gt;
&lt;p&gt;If your JWT is too large, split it into multiple parts and store them across multiple cookies. For example, you can break the JWT into:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Header&lt;/strong&gt;: Store the header part of the JWT in one cookie.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload&lt;/strong&gt;: Store the payload part in another cookie.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signature&lt;/strong&gt;: Store the signature part in a third cookie (if needed).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once the JWT is split and stored, the backend will need to reassemble the parts before validating the token. By managing the token this way, you ensure that each cookie stays within the size limits while still securely storing the JWT.&lt;/p&gt;
&lt;h3 id=&quot;why-this-works&quot;&gt;Why This Works&lt;/h3&gt;
&lt;p&gt;Splitting the JWT ensures that your application remains within browser constraints while leveraging cookies’ security benefits. This method allows you to keep large JWTs while avoiding potential issues with &lt;code&gt;localStorage&lt;/code&gt; or trying to store oversized tokens in a single cookie.&lt;/p&gt;</content:encoded><category>Security</category><category>security</category><category>jwt</category><category>auth</category><author>riyad.abdur@gmail.com (Abdur Rahman)</author></item><item><title>Journey from Pokhara to Khumai Danda: A Beginner’s Himalayan Trek</title><link>https://abdur.tech/writing/pokhara-to-khumai-danda-trek/</link><guid isPermaLink="true">https://abdur.tech/writing/pokhara-to-khumai-danda-trek/</guid><description>Nepal is a land of awe-inspiring landscapes, towering peaks, and pristine mountain trails.</description><pubDate>Tue, 29 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Nepal is a land of awe-inspiring landscapes, towering peaks, and pristine mountain trails. For me, trekking from Pokhara to Khumai Danda was more than just a journey; it was an experience filled with physical challenges, natural beauty, and cultural encounters. Our trek introduced us to the iconic Annapurna and Machapuchare Himalayan mountains, bringing us closer to nature than ever before. Here’s a detailed look at the journey, complete with &lt;strong&gt;photos&lt;/strong&gt; that capture the essence of this unforgettable trek.&lt;/p&gt;
&lt;p&gt;[Image: Route map of the trek with stops from Tushey through Saripakha, Chichimley, Deurali and Lupruchi to Khumai Danda]&lt;/p&gt;
&lt;h2 id=&quot;day-1-starting-the-adventure&quot;&gt;Day 1: Starting the Adventure&lt;/h2&gt;
&lt;p&gt;Our adventure began at a lakeside hotel near Phewa Lake in Pokhara, from where we took a taxi to the trekking checkpoint. After some formalities — such as logging our entry details — we set off on foot. The path was inviting, with the morning sun casting a golden hue on the surrounding mountains.&lt;/p&gt;
&lt;p&gt;[Image: Snow-covered Machapuchare peak rising above village houses, seen from inside a taxi]&lt;/p&gt;
&lt;p&gt;Our first stop came after about an hour of walking, at a cozy rest house where we refueled with a hearty breakfast of chana, boiled eggs, and black tea with a distinctive aroma. The scenery was inspiring, and the journey, initially planned to be a single-day trek, was extended to two days with another day for the descent. With the help of the rest house owner, we arranged for a night’s stay in Chichimley, our stopover for the day.&lt;/p&gt;
&lt;h2 id=&quot;reaching-chichimley-the-uphill-climb&quot;&gt;Reaching Chichimley: The Uphill Climb&lt;/h2&gt;
&lt;p&gt;[Image: Trekkers having tea at an outdoor table by a stone rest house with snowy Himalayan peaks behind]&lt;/p&gt;
&lt;p&gt;After a short break, we began our ascent towards Khumai Danda. The trail quickly became challenging, with steep uphill paths that demanded focus and stamina. After two hours of rigorous climbing, we found another small teahouse where we stopped for some hot tea and enjoyed the sight of the Himalayas inching closer with every step.&lt;/p&gt;
&lt;p&gt;With renewed energy, we pushed on, passing by waterfalls where we made our own tea from the hot water we’d carried. These mini-breaks provided the perfect moments for photos, snacks, and the sheer joy of being surrounded by untouched nature.&lt;/p&gt;
&lt;p&gt;[Image: Group of trekkers resting with cups on stone steps beside a forest stream]&lt;/p&gt;
&lt;p&gt;After a long and challenging walk, we finally reached Chichimley, only to find out that due to a booking miscommunication, there was no room available. The hotel owner graciously set up a tent for us outside. Though the space was tight, the tent became our refuge in the biting cold of the night, amidst an atmosphere that truly felt like wilderness.&lt;/p&gt;
&lt;p&gt;[Image: Blue and orange tent pitched on a grassy ledge beneath large trees]&lt;/p&gt;
&lt;h2 id=&quot;night-at-chichimley-cold-campfire-and-community&quot;&gt;Night at Chichimley: Cold, Campfire, and Community&lt;/h2&gt;
&lt;p&gt;Despite the cold, the night had its warm moments. After dinner, we gathered around a campfire under the stars, sharing stories and laughter with other trekkers. We dined on dal chawal (lentil rice) and vegetables, a comforting meal after a long day of hiking. The night was chilly, but the warmth of camaraderie kept us comfortable enough to rest and recharge.&lt;/p&gt;
&lt;p&gt;[Image: Trekkers around a campfire at night, one playing a flute as sparks rise]&lt;/p&gt;
&lt;h2 id=&quot;day-2-ascending-to-the-peak-of-khumai-danda&quot;&gt;Day 2: Ascending to the Peak of Khumai Danda&lt;/h2&gt;
&lt;p&gt;Early the next morning, we resumed our journey, heading towards Khumai Danda’s peak to witness the grandeur of Annapurna and Machapuchare. The trail became even steeper and more precarious. After a few hours, we reached the rest house at Khumai Danda, where we enjoyed some tea and refilled our water. With our next hotel booked at the core viewpoint, we were eager to make our way to the top.&lt;/p&gt;
&lt;p&gt;[Image: Trekker with a pole on a steep stone-step trail along a grassy ridge with mountains beyond]&lt;/p&gt;
&lt;p&gt;Our trek was soon interrupted by a dramatic moment: a fellow hiker had slipped, injuring her foot. We helped her with our first-aid supplies and continued, humbled by the reminder of nature’s unpredictability. Finally, we reached the summit, surrounded by a 360-degree view of towering Himalayan mountains and lush valleys.&lt;/p&gt;
&lt;p&gt;[Image: Trekker sitting on a grassy summit looking out over layers of misty blue mountain ridges]&lt;/p&gt;
&lt;p&gt;As we celebrated reaching the peak, we discovered one of our friends had accidentally left his jacket at the last rest stop — a tough situation given the cold and proximity to freezing temperatures. After some deliberation, we chose to forgo the core viewpoint hike, accepting the breathtaking views we already had.&lt;/p&gt;
&lt;p&gt;[Image: Four trekkers in jackets posing with thumbs up in front of a snow-capped peak]&lt;/p&gt;
&lt;h2 id=&quot;night-at-khumai-danda-friends-music-and-memories&quot;&gt;Night at Khumai Danda: Friends, Music, and Memories&lt;/h2&gt;
&lt;p&gt;As evening settled, we bonded with fellow trekkers from Nepal who shared our profession in technology. With music echoing through the air, we sang Nepali and Bengali songs, forging unforgettable friendships. The temperature dropped even further, forcing us to stay close together and savor the warmth of the rest house as we enjoyed the night’s last meal.&lt;/p&gt;
&lt;p&gt;[Image: Steel plate of dal bhat: rice with papad, lentil soup, vegetable curry, greens and pickle]&lt;/p&gt;
&lt;h2 id=&quot;day-3-sunrise-and-snowfallthe-journey-back&quot;&gt;Day 3: Sunrise and Snowfall — The Journey Back&lt;/h2&gt;
&lt;p&gt;Our last morning at Khumai Danda was nothing short of magical. We woke up early to witness the sunrise painting the mountains in shades of pink and red. As if that wasn’t enough, light snowfall began — a dream moment for me, seeing snow fall against the Himalayan backdrop.&lt;/p&gt;
&lt;p&gt;[Image: Blue and white tent on a grassy field with the sharp snowy summit of Machapuchare towering behind]&lt;/p&gt;
&lt;p&gt;With our hearts full and bags packed with memories, we began our descent back to Pokhara. On the way, we recovered the forgotten jacket — a small victory that felt symbolic of a journey completed. Tired but fulfilled, we returned to Pokhara after hours of trekking and a brief jeep ride, carrying memories and a deeper respect for the mountains.&lt;/p&gt;
&lt;p&gt;[Image: Trekkers with poles descending steep stone steps through tall grass on a hillside]&lt;/p&gt;
&lt;h2 id=&quot;reflections-a-trekking-journey-to-remember&quot;&gt;Reflections: A Trekking Journey to Remember&lt;/h2&gt;
&lt;p&gt;For a beginner, the trek from Pokhara to Khumai Danda is the perfect introduction to Himalayan trekking. It’s challenging but accessible, offering a balance of adventure, cultural experiences, and natural wonders. This trek has etched itself in my heart as a highlight of my Nepal journey, and I hope to return, ready for an even more ambitious adventure.&lt;/p&gt;</content:encoded><category>Travel</category><category>travel</category><category>trekking</category><author>riyad.abdur@gmail.com (Abdur Rahman)</author></item><item><title>Discovering Nagarkot: A Work-from-the-Hills Adventure</title><link>https://abdur.tech/writing/discovering-nagarkot/</link><guid isPermaLink="true">https://abdur.tech/writing/discovering-nagarkot/</guid><description>Sometimes, a change of pace is exactly what’s needed to clear the mind.</description><pubDate>Tue, 15 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Sometimes, a change of pace is exactly what’s needed to clear the mind. That shift came for me during a trip to &lt;strong&gt;Nagarkot&lt;/strong&gt;, a peaceful hill station just a short distance from the bustling streets of &lt;strong&gt;Thamel&lt;/strong&gt;, Kathmandu. With a laptop in my bag and a curiosity for both adventure and tranquility, I set off for a quick getaway that turned out to be one of the most memorable experiences I’ve had in Nepal.&lt;/p&gt;
&lt;h2 id=&quot;the-journey-from-thamel-to-nagarkot&quot;&gt;The Journey: From Thamel to Nagarkot&lt;/h2&gt;
&lt;p&gt;Leaving Thamel behind in the early morning, I headed out for what was supposed to be a peaceful work trip. What I didn’t expect was how breathtaking the journey itself would be. As the car snaked its way through &lt;strong&gt;hilly roads&lt;/strong&gt;, we passed by &lt;strong&gt;villages that looked frozen in time&lt;/strong&gt;, perched on terraced slopes. The further we went, the more the air changed. It got cooler, fresher, and suddenly it felt like I was worlds away from the city.&lt;/p&gt;
&lt;p&gt;[Image: Long suspension bridge crowded with people stretching across a forested valley]&lt;/p&gt;
&lt;p&gt;There was a point when we crossed a &lt;strong&gt;huge suspension bridge&lt;/strong&gt;, and just below it, there was a &lt;strong&gt;waterfall&lt;/strong&gt; that seemed to appear out of nowhere. The mist rising from the water created this otherworldly atmosphere — it felt like I had stumbled into a scene from a travel documentary. Another waterfall showed up a little later, reminding me how much beauty Nepal packs into its landscapes.&lt;/p&gt;
&lt;p&gt;[Image: Waterfall rushing over dark rocks and boulders surrounded by green foliage]&lt;/p&gt;
&lt;h2 id=&quot;finding-a-work-life-balance-coffee-with-a-view&quot;&gt;Finding a Work-Life Balance: Coffee with a View&lt;/h2&gt;
&lt;p&gt;Once I arrived in Nagarkot, I discovered the perfect place to work — a cozy coffee shop with a terrace that looks out over the hills. Imagine sitting outside with a cup of fresh coffee, typing on your laptop while taking in the beautiful hilly view. If the sky is clear and you’re lucky, you might even see Mount Everest in the distance. It’s the kind of spot that inspires you, whether you’re focused on work or just soaking in the beauty around you.&lt;/p&gt;
&lt;p&gt;[Image: Laptop, breakfast plate and coffee on a cafe table by a window overlooking hills and clouds]&lt;/p&gt;
&lt;p&gt;The coffee shop had all the essentials — good WiFi, warm drinks, and the kind of quiet you need to really focus. And with that view, even work felt a little less like work. It was the ideal blend of productivity and peace. Honestly, if I could do all my remote work from here, I’d never leave.&lt;/p&gt;
&lt;h2 id=&quot;sunset-points-and-everest-views&quot;&gt;Sunset Points and Everest Views&lt;/h2&gt;
&lt;p&gt;Nagarkot is famous for its &lt;strong&gt;sunset points&lt;/strong&gt;, so after wrapping up my work, I made sure to catch one. Watching the sun dip behind the &lt;strong&gt;rolling hills&lt;/strong&gt;, casting a golden glow over the valley, was a moment I won’t soon forget.&lt;/p&gt;
&lt;p&gt;[Image: Orange sunset sky over layered silhouetted hills seen from a balcony]&lt;/p&gt;
&lt;p&gt;But the highlight came the next morning. I woke up early, hoping for a clear sky, and headed to the &lt;strong&gt;Nagarkot Watch Tower&lt;/strong&gt;. As the clouds began to lift, there it was — &lt;strong&gt;Mount Everest&lt;/strong&gt;, peeking through the horizon. It was one of those rare, perfect moments where nature gives you exactly what you’re hoping for.&lt;/p&gt;
&lt;p&gt;[Image: Snow-capped Himalayan peaks on the horizon above treetops under a hazy blue sky]&lt;/p&gt;
&lt;h2 id=&quot;dining-with-a-view&quot;&gt;Dining with a View&lt;/h2&gt;
&lt;p&gt;One evening, I enjoyed dinner at a hilltop restaurant that offered an amazing view. The food was great, but what really stood out was the scenery. From my table, I looked out at the villages below, and as night fell, the valley lit up with twinkling lights, creating a beautiful and peaceful atmosphere.&lt;/p&gt;
&lt;p&gt;[Image: Rooftop restaurant terrace at night with string lights, tire stools and distant valley lights]&lt;/p&gt;
&lt;p&gt;The houses started lighting up one by one, and before long, the entire hillside was twinkling with lights. It was a perfect end to an already perfect day.&lt;/p&gt;
&lt;p&gt;[Image: Old blue van converted into a lit-up seating nook with prayer flags, parked at night]&lt;/p&gt;
&lt;p&gt;Nagarkot is more than just a hill station — it’s a place where you can truly unwind while staying productive. The fresh air, the breathtaking landscapes, and the peaceful vibe make it an ideal destination for remote workers looking for a change of pace. Whether you’re there to work, hike, or simply take in the sunsets, Nagarkot will leave you feeling refreshed and inspired.&lt;/p&gt;
&lt;p&gt;And if you’re lucky, you just might catch Everest watching over you.&lt;/p&gt;</content:encoded><category>Travel</category><category>travel</category><category>remote-work</category><author>riyad.abdur@gmail.com (Abdur Rahman)</author></item></channel></rss>