<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Abolfazl Mohajeri Personal Blog]]></title><description><![CDATA[Abolfazl Mohajeri Thoughts, Notes & Experiences.]]></description><link>https://blog.abolfazlmohajeri.ir</link><image><url>https://cdn.hashnode.com/uploads/logos/64529bee5b3d88bba8b0e0e5/cbe0f061-2320-4a36-bf9a-03ba5e292534.png</url><title>Abolfazl Mohajeri Personal Blog</title><link>https://blog.abolfazlmohajeri.ir</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 12 Sep 2026 04:50:37 GMT</lastBuildDate><atom:link href="https://blog.abolfazlmohajeri.ir/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Learn Claude — Part 8: The API, Automation & Professional Mastery]]></title><description><![CDATA[The final part covers the layer beyond the chat interface — the API and automation — plus the operational knowledge (plans, limits, verification discipline) that makes everything from Parts 1–7 durabl]]></description><link>https://blog.abolfazlmohajeri.ir/learn-claude-part-8-the-api-automation-professional-mastery</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/learn-claude-part-8-the-api-automation-professional-mastery</guid><category><![CDATA[AI]]></category><category><![CDATA[claude]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 20 Aug 2026 09:38:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/d65d730b-38b2-4f90-a0f3-409997b88e90.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The final part covers the layer beyond the chat interface — the <strong>API</strong> and automation — plus the operational knowledge (plans, limits, verification discipline) that makes everything from Parts 1–7 durable. This is the difference between <em>using</em> Claude and <em>building on</em> it.</p>
<h2>The API: Claude as a component</h2>
<p>Everything in this series ran through claude.ai. The <strong>Claude API</strong> exposes the same models programmatically — Claude becomes a building block inside your own scripts, apps, and pipelines.</p>
<p>When does the API beat the chat interface?</p>
<ul>
<li><p><strong>Repetition:</strong> the same operation on 500 inputs (classify tickets, summarize reviews, extract fields from invoices)</p>
</li>
<li><p><strong>Integration:</strong> Claude inside <em>your</em> product or internal tool</p>
</li>
<li><p><strong>Scheduling:</strong> jobs that run without a human present</p>
</li>
<li><p><strong>Precision:</strong> exact control over the system prompt, model, temperature, and output format</p>
</li>
</ul>
<p>The core call is disarmingly small — send messages, receive a response:</p>
<pre><code class="language-python">import anthropic

client = anthropic.Anthropic()  # uses ANTHROPIC_API_KEY

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1000,
    system="You extract invoice data. Respond with JSON only.",
    messages=[{"role": "user", "content": invoice_text}],
)
print(response.content[0].text)
</code></pre>
<p>Wrap that in a loop and you've automated a job. Everything you learned in Part 3 — roles, examples, structured output — <em>is</em> API prompt design; the skills transfer one-to-one. Start at <a href="https://platform.claude.com">platform.claude.com</a> (new accounts get a small free credit) and the docs at <a href="https://docs.claude.com/en/api/overview">docs.claude.com</a>.</p>
<p>Three API-specific concepts to know exist (learn them when needed): <strong>system prompts</strong> (your Project instructions, as a parameter), <strong>tool use / function calling</strong> (Claude decides when to call <em>your</em> functions — how agents are built), and <strong>batch processing</strong> (bulk jobs at lower cost).</p>
<h2>Automation without code</h2>
<p>No programming required to automate:</p>
<ul>
<li><p><strong>Connectors + Projects</strong> (Part 6) already remove most repetitive context work</p>
</li>
<li><p><strong>Automation platforms</strong> (Zapier, Make, n8n) offer Claude steps: "when a form is submitted → Claude summarizes → result lands in Slack"</p>
</li>
<li><p><strong>A disciplined prompt library</strong> (Part 3) is automation of <em>thought</em> — never solve the same prompt twice</p>
</li>
</ul>
<h2>Plans, limits, and economics</h2>
<p>As of mid-2026: <strong>Free</strong> (permanent, real plan — core features included, lower usage caps), <strong>Pro</strong> (~$20/mo — top models, ~5x usage, Claude Code), <strong>Max</strong> ($100–200/mo — same features, much higher headroom), plus Team/Enterprise. The API is billed separately, per token.</p>
<p>The professional intuition: chat plans are <strong>flat-rate thinking time</strong>; the API is <strong>metered production</strong>. Interactive work belongs in chat; volume work belongs in the API. Details drift — verify at <a href="https://claude.com/pricing">claude.com/pricing</a>.</p>
<p>Stretching limits (now with full context from the series): usage scales with tokens processed, so long conversations are expensive by construction → distill and restart (Part 2); match model size to task difficulty (Part 2); put stable context in Projects rather than re-pasting it (Part 6).</p>
<h2>The verification discipline</h2>
<p>The habit separating professionals from the burned: <strong>calibrated trust.</strong> A working checklist —</p>
<ul>
<li><p><strong>Trust freely:</strong> brainstorming, drafts, explanations of well-known concepts, code you will execute anyway (the run <em>is</em> the check)</p>
</li>
<li><p><strong>Verify before use:</strong> specific facts, statistics, quotes, citations, URLs, legal/medical/financial claims</p>
</li>
<li><p><strong>Force verifiability:</strong> ground answers in uploaded documents ("quote the section" — Part 4), compute with code, run the reversal ("argue against your answer" — Part 3)</p>
</li>
<li><p><strong>Never outsource:</strong> the final judgment call. Claude informs decisions; it doesn't own them.</p>
</li>
</ul>
<p>Hallucination isn't a reason to avoid these tools — it's a parameter to engineer around. That mindset <em>is</em> professional AI usage.</p>
<h2>Staying current</h2>
<p>This field moves monthly. Low-effort ways to keep your edge: <a href="https://www.anthropic.com/news">anthropic.com/news</a> for releases, <a href="https://docs.claude.com">docs.claude.com</a> when features shift, and one honest hour of experimenting when something new ships. Principles in this series (context, grounding, iteration, verification) age slowly; feature details age fast.</p>
<h2>The mastery map — the whole series in one view</h2>
<ul>
<li><p><strong>Foundations:</strong> tokens, context window, tools vs. training (Part 1)</p>
</li>
<li><p><strong>Mechanics:</strong> context hygiene, edit-don't-pile, model choice (Part 2)</p>
</li>
<li><p><strong>Prompting:</strong> role/task/context/format → examples, structure, reversal, meta-prompting (Part 3)</p>
</li>
<li><p><strong>Grounding:</strong> documents, vision, computed analysis (Part 4)</p>
</li>
<li><p><strong>Deliverables:</strong> Artifacts, iteration, publishing (Part 5)</p>
</li>
<li><p><strong>Infrastructure:</strong> Projects, memory, connectors (Part 6)</p>
</li>
<li><p><strong>Agency:</strong> Claude Code, plan-first, review discipline (Part 7)</p>
</li>
<li><p><strong>Scale:</strong> API, automation, calibrated trust (Part 8)</p>
</li>
</ul>
<p>If you did the exercises, you didn't read about this — you did it. That was the point.</p>
<h2>Final exercise</h2>
<p>Pick one recurring task from your actual work. Design the full stack for it: which Project, which knowledge files, which prompt template, chat or API, and what verification step. Build it this week. Then teach it to one colleague — teaching is the last stage of mastery.</p>
<p>Thanks for reading the series. Now go build something. 🚀</p>
]]></content:encoded></item><item><title><![CDATA[Learn Claude — Part 7: Coding with Claude & Claude Code]]></title><description><![CDATA[Coding is Claude's most celebrated strength, and it comes in two distinct modes: chat-assisted coding (you drive, Claude advises) and agentic coding with Claude Code (Claude drives, you review). Profe]]></description><link>https://blog.abolfazlmohajeri.ir/learn-claude-part-7-coding-with-claude-claude-code</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/learn-claude-part-7-coding-with-claude-claude-code</guid><category><![CDATA[AI]]></category><category><![CDATA[claude]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 13 Aug 2026 10:16:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/86060258-f84c-4b03-baa1-416b5c21ffbb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Coding is Claude's most celebrated strength, and it comes in two distinct modes: <strong>chat-assisted coding</strong> (you drive, Claude advises) and <strong>agentic coding with Claude Code</strong> (Claude drives, you review). Professionals use both — and know when each applies.</p>
<h2>Mode 1: Chat-assisted coding, done properly</h2>
<p>The four core moves — generate, debug, explain, review — you likely know. What separates effective use is <em>how</em>:</p>
<p><strong>Debugging: give the full picture.</strong> The highest-value debugging prompt contains three things: the <strong>complete error message</strong> (or a screenshot — vision works), the <strong>relevant code</strong>, and <strong>what you already tried</strong>. That last one prevents Claude from suggesting the fix you've already ruled out.</p>
<p><strong>State your environment, always.</strong> "React 18 + TypeScript, no external libraries, must support older browsers" transforms answer quality. Put your permanent stack in Project instructions (Part 6) so you never repeat it.</p>
<p><strong>Review with a rubric.</strong> "Review this" gets generic praise. "Review for: security issues, error handling gaps, and anything that will confuse the next developer — cite line numbers" gets a real review. Then run the reversal from Part 3: <em>"now argue why your suggested refactor might be a mistake."</em></p>
<p><strong>Learn, don't just paste.</strong> The prompt "add comments explaining the <em>why</em> of each block, and tell me one concept I should study" turns every answer into a lesson. Beginners: combined with Artifacts (Part 5), you can build and <em>understand</em> real projects from day one.</p>
<p><strong>Verify by execution.</strong> Claude's code is good but not guaranteed. The loop is: run it → paste any error back → repeat. Fast, and honest.</p>
<h2>Mode 2: Claude Code — the agentic shift</h2>
<p>Chat coding has a ceiling: you are the hands, copying snippets file by file. <strong>Claude Code</strong> removes it. It's a tool from Anthropic that runs in your <strong>terminal</strong> (with desktop and mobile apps too, and IDE integrations) with direct access to your project. Given a task, it:</p>
<ol>
<li><p><strong>Explores</strong> your codebase — reads files, understands structure</p>
</li>
<li><p><strong>Plans</strong> the change and shows you the plan</p>
</li>
<li><p><strong>Executes</strong> — edits multiple files, runs commands and tests</p>
</li>
<li><p><strong>Self-corrects</strong> — sees failing tests and fixes its own work</p>
</li>
<li><p><strong>Reports</strong> — you review the diff, like a pull request from a colleague</p>
</li>
</ol>
<p>The difference in kind: <em>"add password-reset flow to this app"</em> is one instruction, not forty copy-pastes.</p>
<p><strong>Getting started:</strong> requires a paid Claude plan or API access. Install via npm (<code>npm install -g @anthropic-ai/claude-code</code>), run <code>claude</code> inside your repo. Current setup details: <a href="https://docs.claude.com/en/docs/claude-code/overview">docs.claude.com/en/docs/claude-code</a>.</p>
<h2>Working professionally with Claude Code</h2>
<p><strong>Write a CLAUDE.md.</strong> A file in your repo root that Claude Code reads automatically — your Project instructions, but for code: build commands, conventions, architecture notes, "never touch X" warnings. Ten minutes writing it pays back on every session.</p>
<p><strong>Plan before executing.</strong> For non-trivial tasks, ask for a plan first and approve it before edits begin. Steering a plan is cheap; unwinding a wrong implementation is not.</p>
<p><strong>Scope tasks like tickets.</strong> "Fix the timezone bug in report generation" succeeds. "Make the app better" wanders. If you couldn't hand it to a junior dev as written, don't hand it to the agent.</p>
<p><strong>Review diffs like a reviewer, not a spectator.</strong> You remain the engineer of record. Agentic speed plus rubber-stamp review is how subtle bugs ship. Tests, permissions on risky commands, and git are your safety rails — commit before big agent sessions.</p>
<p><strong>Know the escalation ladder.</strong> Quick question → chat. Single-file snippet → chat + Artifact. Multi-file feature, refactor, test-suite work → Claude Code. Choosing the right rung <em>is</em> the skill.</p>
<h2>A realistic session</h2>
<pre><code class="language-plaintext">you:    (in repo) claude
you:    Our /export endpoint times out on large datasets.
        Find the cause and propose a fix — plan first.
claude: [reads code] The endpoint loads all rows into memory...
        Plan: stream results, add pagination, update 2 tests. Proceed?
you:    Proceed.
claude: [edits 3 files, runs tests, one fails, fixes it]
        Done — diff ready for review.
</code></pre>
<p>That's the shape of modern development: you supply judgment and intent; the agent supplies throughput.</p>
<h2>Exercise</h2>
<p><strong>Non-developers:</strong> in chat, build a small web page in an Artifact, then ask Claude to explain every block and quiz you on it. <strong>Developers:</strong> install Claude Code on a side project, write a minimal CLAUDE.md, and delegate one well-scoped ticket — plan-first. Review the diff critically. Form your own opinion of where the ceiling is.</p>
<h2>Next up</h2>
<p><strong>Part 8, the finale:</strong> the API, automation, and the habits that make all of this durable — the full professional's toolkit.</p>
]]></content:encoded></item><item><title><![CDATA[Learn Claude — Part 6: Projects, Memory & Connectors — Your Claude Operating System]]></title><description><![CDATA[Everything so far happens inside one conversation. Professional usage means building infrastructure around conversations: persistent context, standing instructions, and live connections to your actual]]></description><link>https://blog.abolfazlmohajeri.ir/learn-claude-part-6-projects-memory-connectors-your-claude-operating-system</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/learn-claude-part-6-projects-memory-connectors-your-claude-operating-system</guid><category><![CDATA[AI]]></category><category><![CDATA[claude]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Wed, 05 Aug 2026 17:24:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/dc97ea3d-2bbf-436c-845b-3218624c3fac.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Everything so far happens inside one conversation. Professional usage means building <strong>infrastructure around conversations</strong>: persistent context, standing instructions, and live connections to your actual tools. This is the part where Claude stops being an app you visit and becomes a system you work in.</p>
<h2>Projects: persistent context done right</h2>
<p>A <strong>Project</strong> bundles three things for an ongoing area of work:</p>
<ol>
<li><p><strong>Knowledge</strong> — files and notes uploaded once, visible to <em>every</em> chat inside the Project</p>
</li>
<li><p><strong>Custom instructions</strong> — standing rules for how Claude behaves there</p>
</li>
<li><p><strong>Grouped chats</strong> — everything related, in one place</p>
</li>
</ol>
<p>The payoff compounds: instead of re-explaining your situation in every conversation, the context is <em>ambient</em>. A "Job Search" project holds your CV and target roles; every new chat starts already briefed. Say only <em>"cover letter for this posting: [paste]"</em>.</p>
<h2>Engineering your Project instructions</h2>
<p>This is where most people underperform. Instructions are a <strong>system prompt you control</strong> — treat them with Part 3-level care. A professional template:</p>
<pre><code class="language-plaintext">ROLE: You are my [editor / analyst / senior dev reviewing my code].
CONTEXT: [2–3 lines: who I am, what this project is]
ALWAYS: [output format, tone, language level, length defaults]
NEVER: [emojis, filler praise, unexplained jargon]
WHEN UNSURE: Ask me one clarifying question instead of guessing.
</code></pre>
<p>Rules of thumb, learned the hard way:</p>
<ul>
<li><p><strong>Short beats long.</strong> 10 sharp lines outperform two rambling pages — every line is context spent on every message.</p>
</li>
<li><p><strong>Knowledge is for facts, instructions are for behavior.</strong> Your pricing sheet is knowledge; "always quote prices in EUR" is an instruction.</p>
</li>
<li><p><strong>Stale knowledge poisons silently.</strong> An outdated CV in the project means confidently outdated cover letters. Audit your Project files monthly.</p>
</li>
</ul>
<h2>Project architectures that work</h2>
<ul>
<li><p><strong>The Blog Project</strong> — knowledge: style guide, audience description, past top posts. Instructions: voice rules, formatting, SEO checklist. Every draft starts 80% on-brand. (This series is written inside one.)</p>
</li>
<li><p><strong>The Codebase Companion</strong> — knowledge: architecture notes, conventions doc, key schemas. Instructions: "match our patterns; flag breaking changes explicitly."</p>
</li>
<li><p><strong>The Client Project</strong> (one per client) — knowledge: brief, contract scope, correspondence summaries. Instructions: their tone, their terminology. Context-switching between clients becomes instant.</p>
</li>
</ul>
<h2>Memory and preferences: the cross-cutting layer</h2>
<p>Outside Projects, two account-wide layers travel with you:</p>
<ul>
<li><p><strong>Preferences (Settings)</strong> — your global standing instructions, applied everywhere</p>
</li>
<li><p><strong>Memory</strong> — Claude can retain useful details across conversations. Professionals <em>curate</em> it: review what's stored in Settings, delete the noise, keep the durable facts. Treat it like a colleague's notes about you — accurate and current, or corrected.</p>
</li>
</ul>
<p>Precedence intuition: Preferences set your global defaults; Project instructions specialize them; the conversation itself can override both.</p>
<h2>Connectors: Claude reaches into your tools</h2>
<p>The final layer, and the most quietly transformative. <strong>Connectors</strong> (built on an open standard called <strong>MCP — Model Context Protocol</strong>) let Claude securely access other apps you authorize: Google Drive, Gmail, Calendar, Notion, GitHub, Slack, Asana, and a growing directory.</p>
<p>The shift in what a prompt can be:</p>
<ul>
<li><p>"Find our Q3 planning doc in Drive and summarize the open decisions."</p>
</li>
<li><p>"Check my calendar and draft a reply proposing times I'm actually free."</p>
</li>
<li><p>"Look at the last 20 GitHub issues and cluster them by root cause."</p>
</li>
</ul>
<p>No copy-paste. Claude reads (and, where you permit, acts) at the source. You enable connectors in Settings and authorize each one explicitly.</p>
<p><strong>Professional caution, stated plainly:</strong> a connector that can <em>act</em> (send email, edit tasks) deserves more skepticism than one that only <em>reads</em>. Grant minimal scopes, and review before Claude executes anything irreversible. Power tools, respected.</p>
<h2>The complete stack</h2>
<p>Notice what you've assembled across six parts:</p>
<blockquote>
<p><strong>Preferences</strong> (who you are, always) → <strong>Project</strong> (this domain's knowledge + rules) → <strong>Connectors</strong> (live data) → <strong>Conversation</strong> (the task at hand) → <strong>Artifact</strong> (the deliverable)</p>
</blockquote>
<p>That stack — not any single feature — is what "professional Claude usage" actually means.</p>
<h2>Exercise</h2>
<p>Build one real Project this week using the instructions template above: 3 knowledge files, 8–10 instruction lines. Then connect one read-only connector (Drive or Calendar) and run a prompt that touches both. Note how short your prompts have become — that's infrastructure doing the work.</p>
<h2>Next up</h2>
<p><strong>Part 7:</strong> coding — from chat-assisted programming to <strong>Claude Code</strong>, the agentic tool that works in your repository like a colleague.</p>
]]></content:encoded></item><item><title><![CDATA[Learn Claude — Part 5: Artifacts — From Documents to Working Apps]]></title><description><![CDATA[Chat answers evaporate. Artifacts persist: documents, code, diagrams, and fully working apps that appear in a panel beside the conversation, get iterated like real work products, and can be downloaded]]></description><link>https://blog.abolfazlmohajeri.ir/learn-claude-part-5-artifacts-from-documents-to-working-apps</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/learn-claude-part-5-artifacts-from-documents-to-working-apps</guid><category><![CDATA[AI]]></category><category><![CDATA[claude]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 30 Jul 2026 17:42:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/30fd8c27-52fa-4c41-8ca2-60d70faca8b8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Chat answers evaporate. <strong>Artifacts</strong> persist: documents, code, diagrams, and fully working apps that appear in a panel beside the conversation, get iterated like real work products, and can be downloaded or published. This is where Claude stops being a chatbot and becomes a workbench.</p>
<h2>The mental model</h2>
<p>The chat is the <em>discussion</em>; the Artifact is the <em>deliverable</em>. Claude creates one automatically for substantial standalone content, or on request: <em>"put this in an artifact."</em> What can live there:</p>
<ul>
<li><p><strong>Documents</strong> — reports, guides, long-form drafts</p>
</li>
<li><p><strong>Code</strong> — scripts, components, entire single-file apps</p>
</li>
<li><p><strong>Interactive apps</strong> — real HTML/React that <em>runs live in the panel</em></p>
</li>
<li><p><strong>Diagrams</strong> — flowcharts, architecture sketches, visualizations</p>
</li>
<li><p><strong>Tools &amp; games</strong> — calculators, quizzes, trackers, prototypes</p>
</li>
</ul>
<h2>Iteration is the entire point</h2>
<p>The workflow that makes Artifacts powerful:</p>
<blockquote>
<p><strong>You:</strong> Build a landing page for a freelance photographer. <em>(working page appears)</em> <strong>You:</strong> Dark theme, add a portfolio grid. <em>(same page, updated)</em> <strong>You:</strong> The contact button should open a mail link. <em>(updated again)</em></p>
</blockquote>
<p>Claude edits the existing Artifact rather than regenerating it, and you can flip back through <strong>versions</strong> to compare or recover. Two rules for smooth iteration:</p>
<ol>
<li><p><strong>One change per message.</strong> "Make it dark, add a grid, fix the font, and reorder sections" invites collateral damage. Sequential single changes stay precise.</p>
</li>
<li><p><strong>Point at things by name.</strong> "In the pricing section, make the middle card highlighted" beats "make it look better."</p>
</li>
</ol>
<h2>Building without knowing how to code</h2>
<p>Describe <strong>outcomes</strong>, not technology — Claude picks the stack:</p>
<ul>
<li><p>"A habit tracker where I check off daily habits and see a weekly streak."</p>
</li>
<li><p>"An interactive quiz: 10 questions on JavaScript basics, explain each wrong answer."</p>
</li>
<li><p>"A tip-splitting calculator that works well on a phone."</p>
</li>
</ul>
<p>The result runs instantly in the panel. You test it by using it, and request changes in plain language. For many small internal tools, this is legitimately faster than searching for an existing app.</p>
<p>Two power-ups worth knowing:</p>
<ul>
<li><p><strong>Design direction works.</strong> "Brutalist," "like a printed newspaper," "playful pastel," "match this screenshot" (attach one — vision from Part 4 applies here too).</p>
</li>
<li><p><strong>Artifacts can be AI-powered.</strong> You can ask for an app that <em>itself</em> talks to Claude — e.g., "build a flashcard app where Claude generates new cards about any topic I type." Apps that think, built by chatting.</p>
</li>
</ul>
<h2>Publishing and reuse</h2>
<p>From the Artifact panel you can <strong>copy</strong>, <strong>download</strong>, or <strong>publish</strong> — publishing creates a shareable link where others can view and even use interactive artifacts. A quiz for your students, a calculator for your clients, a prototype for your team: shipped from a conversation.</p>
<h2>For developers: the honest scope</h2>
<p>Artifacts are ideal for prototypes, single-file tools, visualizations, and UI experiments. They are <em>not</em> a full dev environment — no databases, no server-side code, limited external libraries. The professional pattern: <strong>prototype the interaction in an Artifact, validate it, then move to a real codebase</strong> — with Claude Code (Part 7) doing the heavy lifting there. Prototype in minutes, build properly afterward.</p>
<h2>A realistic workflow: the client-proposal machine</h2>
<ol>
<li><p>Chat: discuss the project scope (using Part 3's prompting).</p>
</li>
<li><p>"Create an artifact: a structured proposal document from everything above."</p>
</li>
<li><p>Iterate: tighten the timeline section, add a pricing table.</p>
</li>
<li><p>"Now create a second artifact: a one-page interactive summary I can publish and send as a link."</p>
</li>
</ol>
<p>Deliverable <em>and</em> delivery mechanism, one conversation.</p>
<h2>Exercise</h2>
<p>Build something real for yourself this week — a unit converter you actually need, a study quiz, a reading tracker. Then request three iterations and publish it. The moment you send your own link to someone is the moment Artifacts click.</p>
<h2>Next up</h2>
<p><strong>Part 6:</strong> the professional operating system — Projects, memory, and connectors that plug Claude into your actual tools.</p>
]]></content:encoded></item><item><title><![CDATA[Learn Claude — Part 4: Files, Vision & Real Data Analysis]]></title><description><![CDATA[Typed prompts only scratch the surface. Claude's practical power multiplies when you feed it your material — documents, screenshots, spreadsheets — and this is where the hallucination problem from Par]]></description><link>https://blog.abolfazlmohajeri.ir/learn-claude-part-4-files-vision-real-data-analysis</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/learn-claude-part-4-files-vision-real-data-analysis</guid><category><![CDATA[AI]]></category><category><![CDATA[claude]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 23 Jul 2026 13:49:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/1225e7bc-8e05-4472-be4b-b54f69531133.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Typed prompts only scratch the surface. Claude's practical power multiplies when you feed it <strong>your material</strong> — documents, screenshots, spreadsheets — and this is where the hallucination problem from Part 1 gets its most important fix.</p>
<h2>Grounding: why files change everything</h2>
<p>When Claude answers from training data, it's recalling — and recall can be wrong. When Claude answers <strong>from a document in its context window</strong>, it's reading. Answers become checkable against a source you both share. Professionals exploit this constantly:</p>
<blockquote>
<p>Instead of <em>"What does GDPR say about data retention?"</em> (recall, hallucination-prone) upload the actual regulation and ask <em>"According to this document, what are the data-retention rules? Quote the relevant sections."</em> (reading, verifiable)</p>
</blockquote>
<p>The instruction <strong>"answer only from the document, and say if it's not covered"</strong> is one of the most valuable sentences in this series.</p>
<h2>Documents: the core workflows</h2>
<p>Drag any file into the chat — PDFs, Word docs, text, code. The moves that matter:</p>
<ul>
<li><p><strong>Targeted extraction:</strong> "List every deadline, penalty, and obligation in this contract as a table."</p>
</li>
<li><p><strong>Guided summary:</strong> "Summarize this 60-page report <em>for someone deciding whether to fund the project</em>." A summary's audience changes what belongs in it — always state one.</p>
</li>
<li><p><strong>Interrogation:</strong> "What does this lease say about early termination? Quote it." Follow-ups are free; the file stays in context.</p>
</li>
<li><p><strong>Cross-document work:</strong> upload two versions and ask "What changed between these, and which changes shift risk to me?" Multi-file comparison is something humans are slow at and Claude is fast at.</p>
</li>
<li><p><strong>Transformation:</strong> meeting notes → action-item email; research paper → blog outline; requirements doc → test checklist.</p>
</li>
</ul>
<h2>Vision: Claude can genuinely see</h2>
<p>Uploaded images aren't decorations — Claude analyzes them:</p>
<ul>
<li><p><strong>Screenshot of an error</strong> → "Diagnose and fix." (Often faster than copying the text.)</p>
</li>
<li><p><strong>Whiteboard photo</strong> → "Turn this into structured notes with action items."</p>
</li>
<li><p><strong>A chart</strong> → "What's the trend, and what's misleading about how this is presented?"</p>
</li>
<li><p><strong>UI screenshot</strong> → "Critique this design for usability."</p>
</li>
<li><p><strong>Handwriting, foreign-language forms</strong> → transcribe, translate, explain each field.</p>
</li>
</ul>
<p>One built-in boundary: Claude won't identify real people in photos.</p>
<h2>The professional layer: executed data analysis</h2>
<p>Here's the distinction that separates casual use from real analysis. From Part 1: Claude's mental arithmetic is unreliable. The fix is built in — the <strong>analysis tool / code execution</strong> (enable it in Settings if needed). When it's relevant, Claude <strong>writes actual code, runs it on your file, and reports the computed results.</strong></p>
<p>Upload a CSV and try:</p>
<ul>
<li><p>"Compute monthly revenue growth and flag anomalies. Use code, show your work."</p>
</li>
<li><p>"Clean this data: trim whitespace, standardize dates, list duplicates — then give me the cleaned file."</p>
</li>
<li><p>"Which two columns correlate most strongly? Plot it."</p>
</li>
</ul>
<p>The output is the difference between <em>"revenue seems to grow around 10%"</em> (estimated, maybe hallucinated) and <em>"revenue grew 11.4% month-over-month"</em> (computed). When numbers matter, the magic phrase is <strong>"use code to calculate this."</strong></p>
<h2>Knowing the limits</h2>
<ol>
<li><p><strong>Size ceilings exist.</strong> Very large files can exceed the context window. Strategies: split by chapter, or summarize sections in separate chats and synthesize the summaries (context distillation again, from Part 2).</p>
</li>
<li><p><strong>Long-document attention.</strong> In huge documents, details buried mid-file get less attention than the start and end. For contracts and compliance work, ask section-targeted questions rather than one giant "check everything."</p>
</li>
<li><p><strong>Scanned PDFs vary.</strong> Claude handles them, but low-quality scans degrade extraction — spot-check quotes against the page.</p>
</li>
<li><p><strong>Verification discipline stays.</strong> Grounding shrinks hallucination; it doesn't abolish it. For consequential documents, verify quoted passages exist. The habit costs a minute.</p>
</li>
</ol>
<h2>A realistic workflow: competitor research</h2>
<ol>
<li><p>Upload three competitor pricing PDFs → "Extract all plans and prices into one comparison table."</p>
</li>
<li><p>"Use code: compute the average price per feature tier."</p>
</li>
<li><p>"Which competitor changed positioning compared to this older PDF?" (upload it)</p>
</li>
<li><p>End with distillation: "Summarize the strategic picture in 10 bullets" → carry that into a fresh chat for strategy work.</p>
</li>
</ol>
<p>Four prompts. That used to be an afternoon.</p>
<h2>Exercise</h2>
<p>Take a real spreadsheet or export (bank statement, sales data, anything). Ask one question <em>without</em> code, then the same question <em>with</em> "use code to calculate this." Compare the answers — this lesson sticks best when you see the difference yourself.</p>
<h2>Next up</h2>
<p><strong>Part 5:</strong> Artifacts — turning conversations into working documents, tools, and apps you can actually ship.</p>
]]></content:encoded></item><item><title><![CDATA[Learn Claude — Part 3: Prompting, From Basics to Advanced Techniques]]></title><description><![CDATA[Prompting is the highest-leverage skill in this entire series. We'll build it in three levels — start where you are.
Level 1: The foundation formula
Every solid prompt covers four things:

Role + Task]]></description><link>https://blog.abolfazlmohajeri.ir/learn-claude-part-3-prompting-from-basics-to-advanced-techniques</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/learn-claude-part-3-prompting-from-basics-to-advanced-techniques</guid><category><![CDATA[AI]]></category><category><![CDATA[claude]]></category><category><![CDATA[Prompt Engineering]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 16 Jul 2026 10:06:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/1b225ae1-8326-4efe-a296-0e46c703c03b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Prompting is the highest-leverage skill in this entire series. We'll build it in three levels — start where you are.</p>
<h2>Level 1: The foundation formula</h2>
<p>Every solid prompt covers four things:</p>
<blockquote>
<p><strong>Role + Task + Context + Format</strong></p>
</blockquote>
<p>❌ "Write a blog post about fitness."</p>
<p>✅ "You're a fitness coach who writes for busy professionals <em>(role)</em>. Write a blog post about 10-minute morning workouts <em>(task)</em>. Audience: office workers with no equipment, mostly beginners <em>(context)</em>. 600 words, friendly tone, numbered exercise list <em>(format)</em>."</p>
<p>Specificity isn't decoration — every constraint eliminates thousands of wrong answers. If you learn nothing else, learn this formula. Now let's go further.</p>
<h2>Level 2: Intermediate techniques</h2>
<h3>Few-shot prompting: show, don't describe</h3>
<p>The single biggest upgrade from Level 1. Instead of <em>describing</em> the style you want, <strong>show 1–3 examples</strong>:</p>
<pre><code class="language-plaintext">Rewrite product names in our brand voice. Examples:

"Steel water bottle 750ml" → "The Everyday Bottle — built for your whole day"
"Wireless mouse M2" → "The Quiet Click — work without the noise"

Now rewrite: "Ergonomic office chair X200"
</code></pre>
<p>Claude is exceptional at pattern-matching. Two good examples routinely outperform two paragraphs of instructions.</p>
<h3>Structure with delimiters</h3>
<p>When prompts mix instructions with data (a document, an email, code), separate them clearly. Claude is specifically trained to respect <strong>XML-style tags</strong>:</p>
<pre><code class="language-plaintext">Summarize the customer complaint below in 3 bullets,
then classify severity as low/medium/high.

&lt;complaint&gt;
[paste the full text here]
&lt;/complaint&gt;
</code></pre>
<p>Now "summarize" can never bleed into the complaint text, and long inputs stay unambiguous. This habit matters more the bigger your inputs get.</p>
<h3>Positive instructions beat negative ones</h3>
<p>"Don't be formal" forces Claude to guess what you <em>do</em> want. "Write like you're texting a smart friend" nails it. Say what to do, not only what to avoid.</p>
<h3>Constrain the output shape</h3>
<p>"Respond as a markdown table with columns X, Y, Z", "exactly 5 options, one line each", "return only valid JSON, no explanation". If you'll reuse the output somewhere (a spreadsheet, code, your blog), specify the shape and skip the cleanup.</p>
<h2>Level 3: Advanced techniques</h2>
<h3>Make Claude think before answering</h3>
<p>For analysis, evaluation, or anything with a judgment call:</p>
<blockquote>
<p>"Before giving your recommendation, first reason through the pros and cons of each option step by step. Then give your final answer with a confidence level."</p>
</blockquote>
<p>Forcing visible intermediate reasoning measurably improves final answers — and lets you audit <em>where</em> the reasoning went wrong when it does. (For truly hard problems, combine this with the extended-thinking toggle from Part 2.)</p>
<h3>Prompt the reversal</h3>
<p>Ask Claude to argue against itself: <em>"Now make the strongest case against your own recommendation."</em> This is the fastest hallucination- and blind-spot-check that exists, and professionals use it constantly for decisions that matter.</p>
<h3>Meta-prompting: recursion that actually works</h3>
<p>Claude is excellent at improving prompts — including yours:</p>
<blockquote>
<p>"I want Claude to generate weekly LinkedIn posts from my rough notes. Write the ideal reusable prompt for this. Ask me clarifying questions first."</p>
</blockquote>
<p>Answer its questions, get a polished prompt, save it as a template. You've just automated your own prompt engineering.</p>
<h3>Build a prompt library</h3>
<p>The final professional habit: <strong>stop rewriting prompts you've already perfected.</strong> Keep a note file (or, better, Project instructions — Part 6) of your proven templates: the report summarizer, the code reviewer, the email drafter. Over months, this library becomes a genuine productivity asset.</p>
<h2>Debugging a bad answer: a checklist</h2>
<p>When output disappoints, check in order:</p>
<ol>
<li><p><strong>Ambiguity</strong> — could my request mean two things? (Most common cause.)</p>
</li>
<li><p><strong>Missing context</strong> — does Claude know what I know?</p>
</li>
<li><p><strong>No example</strong> — would one sample fix the style?</p>
</li>
<li><p><strong>Contaminated conversation</strong> — is an earlier wrong turn still in context? → <em>edit</em> the message (Part 2), don't pile on corrections</p>
</li>
<li><p><strong>Wrong model / thinking off</strong> — hard problem on a fast setting?</p>
</li>
</ol>
<h2>Exercise</h2>
<p>Take a task you do weekly. Write a Level 1 prompt, then upgrade it with one example and XML structure, then run the meta-prompting technique on it. Compare all three outputs — and save the winner to your new prompt library.</p>
<h2>Next up</h2>
<p><strong>Part 4:</strong> feeding Claude real material — documents, images, spreadsheets — and running genuine data analysis with executed code.</p>
]]></content:encoded></item><item><title><![CDATA[Learn Claude — Part 2: Setup, Interface & Conversation Mechanics]]></title><description><![CDATA[Signing up takes two minutes. What separates casual users from effective ones isn't the interface — it's conversation mechanics: how you manage context, when you branch, when you restart, and which se]]></description><link>https://blog.abolfazlmohajeri.ir/learn-claude-part-2-setup-interface-conversation-mechanics</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/learn-claude-part-2-setup-interface-conversation-mechanics</guid><category><![CDATA[AI]]></category><category><![CDATA[claude]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Fri, 10 Jul 2026 07:15:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/df5d5b54-ebd2-4827-ae96-7148c0349b08.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Signing up takes two minutes. What separates casual users from effective ones isn't the interface — it's <strong>conversation mechanics</strong>: how you manage context, when you branch, when you restart, and which settings you've configured. That's what this part is really about.</p>
<h2>The two-minute setup</h2>
<p>Go to <a href="https://claude.ai"><strong>claude.ai</strong></a>, sign up with email, Google, or Apple. The free plan is permanent, no card required. Install the <strong>mobile app</strong> and, if you work at a computer all day, the <strong>desktop app</strong> — a global keyboard shortcut to summon Claude changes how often you actually use it.</p>
<p>Interface essentials, quickly: the <strong>message box</strong>, the <strong>sidebar</strong> (past chats, searchable), the <strong>model picker</strong> dropdown, and the <strong>attach button</strong> for files. Done. Now the part that matters.</p>
<h2>Mechanic 1: Every message replays the whole conversation</h2>
<p>From Part 1: Claude has no hidden memory — the conversation transcript <em>is</em> its memory, re-read on every turn. Three consequences:</p>
<ol>
<li><p><strong>Long chats degrade.</strong> Responses slow down, usage burns faster, and details from 50 messages ago get less attention.</p>
</li>
<li><p><strong>Everything in the chat influences everything after.</strong> A wrong assumption Claude made early keeps echoing unless you correct it explicitly.</p>
</li>
<li><p><strong>A fresh chat is a reset button</strong> — often the strongest debugging move you have.</p>
</li>
</ol>
<h2>Mechanic 2: When to continue vs. when to restart</h2>
<p><strong>Continue the same chat when</strong> you're iterating on the same task and the accumulated context helps ("now make section 2 more formal").</p>
<p><strong>Start a new chat when:</strong></p>
<ul>
<li><p>You're switching topics — even related ones</p>
</li>
<li><p>The conversation went sideways and corrections aren't sticking</p>
</li>
<li><p>You've finished a phase (research done → new chat for writing, pasting in only the summary)</p>
</li>
</ul>
<p>That last one is a professional pattern called <strong>context distillation</strong>: end a long session with <em>"Summarize everything we decided as a brief I can paste into a new conversation"</em> — then start clean with just that brief. Best of both worlds: full memory of decisions, none of the clutter.</p>
<h2>Mechanic 3: Edit, don't pile up</h2>
<p>Most people respond to a bad answer by adding another message: "no, I meant..." — which leaves the misunderstanding <em>in</em> the context, still influencing everything.</p>
<p>The stronger move: <strong>edit your original message</strong> (hover over it) and rephrase. The conversation re-branches from that point as if the bad turn never happened. You can also <strong>retry</strong> a Claude response to get a different take. Cleaning history beats correcting it.</p>
<h2>Mechanic 4: Choose models deliberately</h2>
<p>The model picker isn't decoration:</p>
<ul>
<li><p><strong>Fast models (Haiku/Sonnet)</strong> for drafts, summaries, simple questions — quicker <em>and</em> lighter on your usage limits</p>
</li>
<li><p><strong>Top models (Opus and above)</strong> for complex reasoning, tricky code, high-stakes writing</p>
</li>
</ul>
<p>You can <strong>switch mid-conversation</strong> — the new model sees the full history. Pattern: explore cheap, finish strong.</p>
<p>Also try <strong>extended thinking</strong> (a toggle, on supported models) for genuinely hard problems — Claude reasons step-by-step before answering. Slower, but noticeably better on math, logic, and planning. Leave it off for casual questions.</p>
<h2>Mechanic 5: Configure once, benefit forever</h2>
<p>Five minutes in <strong>Settings</strong> pays off permanently:</p>
<ul>
<li><p><strong>Preferences</strong> — standing instructions applied to every chat: "I'm a non-native English speaker, keep language natural and simple. Never use emojis. When I ask for code, always include comments."</p>
</li>
<li><p><strong>Styles</strong> — switch response style (concise / explanatory / formal) per task, or create your own from writing samples</p>
</li>
<li><p><strong>Memory</strong> — lets Claude carry useful details across conversations; review and edit what's stored in Settings</p>
</li>
<li><p><strong>Feature toggles</strong> — make sure web search and the analysis tool are enabled; you'll need both later in this series</p>
</li>
</ul>
<h2>A worked example</h2>
<p>❌ <em>Novice:</em> one 80-message chat mixing a trip plan, a CV review, and a Python bug — by the end, Claude is confusing the contexts.</p>
<p>✅ <em>Practitioner:</em> three chats. The trip chat ends with "summarize the itinerary decisions"; that summary starts a clean booking-research chat, on a fast model, with web search on.</p>
<p>Same tool. Completely different results.</p>
<h2>Exercise</h2>
<ol>
<li><p>Set up your Preferences (3 lines minimum).</p>
</li>
<li><p>Start a chat on any topic, then deliberately <strong>edit</strong> an earlier message and watch the conversation re-branch.</p>
</li>
<li><p>End a chat with the context-distillation prompt and carry the summary into a new one.</p>
</li>
</ol>
<h2>Next up</h2>
<p><strong>Part 3:</strong> prompting — from the basic formula to the structured, example-driven techniques professionals use.</p>
]]></content:encoded></item><item><title><![CDATA[Learn Claude — Part 1: What Claude Is & How It Actually Works]]></title><description><![CDATA[Welcome to Learn Claude, a series that takes you from your first message to professional-level workflows: advanced prompting, data analysis, building apps, Claude Code, and the API. This first post bu]]></description><link>https://blog.abolfazlmohajeri.ir/learn-claude-part-1-what-claude-is-how-it-actually-works</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/learn-claude-part-1-what-claude-is-how-it-actually-works</guid><category><![CDATA[AI]]></category><category><![CDATA[claude]]></category><category><![CDATA[#anthropic]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Fri, 03 Jul 2026 16:35:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/985ba075-5481-453c-9be0-f6c1dc65eb03.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to <strong>Learn Claude</strong>, a series that takes you from your first message to professional-level workflows: advanced prompting, data analysis, building apps, Claude Code, and the API. This first post builds the mental model everything else depends on. Skip it, and the later parts will feel like magic tricks. Read it, and they'll feel obvious.</p>
<h2>What is Claude?</h2>
<p>Claude is an AI assistant built by <strong>Anthropic</strong>, an AI safety company. On the surface, it's a chat interface: you write, it answers. Underneath, it's a <strong>large language model (LLM)</strong> — a neural network trained on enormous amounts of text to predict what comes next in a sequence.</p>
<p>That "predict the next word" framing sounds trivial, but at sufficient scale it produces something remarkable: a system that can reason through problems, write working code, analyze documents, and explain almost anything. You don't need the math — but three concepts <em>will</em> make you dramatically better at using it.</p>
<h2>Concept 1: Tokens — how Claude reads</h2>
<p>Claude doesn't see letters or words. Text is broken into <strong>tokens</strong> — chunks of roughly 3–4 English characters. "Understanding" might be two tokens; "cat" is one.</p>
<p>Why should you care?</p>
<ul>
<li><p><strong>Limits are measured in tokens.</strong> Message limits, document sizes, response lengths — all token-based.</p>
</li>
<li><p><strong>Cost (on the API) is per token.</strong> Verbose prompts literally cost more.</p>
</li>
<li><p><strong>It explains quirks.</strong> Counting letters in a word is oddly hard for LLMs because they never see individual letters.</p>
</li>
</ul>
<h2>Concept 2: The context window — Claude's working memory</h2>
<p>Everything Claude can "see" during a conversation — your messages, its replies, uploaded files, instructions — lives in the <strong>context window</strong>. Claude's is huge (hundreds of pages of text), which is one of its signature strengths.</p>
<p>Two professional-grade implications:</p>
<ol>
<li><p><strong>Claude re-reads the entire conversation on every message.</strong> Nothing is "remembered" between turns in some hidden brain — the memory <em>is</em> the transcript. This is why long chats slow down and consume usage limits faster.</p>
</li>
<li><p><strong>When context fills up, quality degrades before it fails.</strong> Details from early in a very long conversation get less attention. Pros start fresh chats deliberately and re-supply only the relevant context.</p>
</li>
</ol>
<p>We'll turn this into concrete conversation-management tactics in Part 2.</p>
<h2>Concept 3: Training cutoff vs. tools</h2>
<p>Claude's base knowledge stops at its <strong>training cutoff date</strong>. It doesn't inherently know today's news, prices, or your company's data. What closes the gap is <strong>tools</strong>: web search, file uploads, code execution, and connectors to apps like Google Drive. A large part of professional Claude usage is knowing <em>which knowledge lives in the model</em> and <em>which must be brought in via tools</em>. This series covers all of them.</p>
<h2>The model family</h2>
<p>Claude ships in several models — a speed/capability trade-off:</p>
<ul>
<li><p><strong>Haiku</strong> — fastest and cheapest; ideal for simple, high-volume tasks</p>
</li>
<li><p><strong>Sonnet</strong> — the balanced workhorse for everyday work</p>
</li>
<li><p><strong>Opus and above</strong> — maximum capability for complex reasoning, hard coding problems, and long documents</p>
</li>
</ul>
<p>You can switch models mid-conversation from a dropdown. A practical pattern you'll use later: draft and explore with a fast model, then switch up for the hard final step.</p>
<h2>What Claude is genuinely good and bad at</h2>
<p><strong>Strong:</strong> writing and editing, coding, summarizing and analyzing long documents, structured reasoning, translation with natural phrasing, brainstorming, explaining at any level.</p>
<p><strong>Weak — know these cold:</strong></p>
<ul>
<li><p><strong>Hallucination.</strong> Claude can state false things fluently and confidently — especially niche facts, statistics, citations, and URLs. Verification is <em>your</em> job; we'll build it into every workflow in this series.</p>
</li>
<li><p><strong>Arithmetic at scale.</strong> For real calculations, have Claude write and run code (Part 4) instead of computing "in its head."</p>
</li>
<li><p><strong>Real-time anything</strong> — without web search enabled.</p>
</li>
</ul>
<h2>The one-sentence summary</h2>
<blockquote>
<p>Claude is a reasoning engine with a large but finite working memory, frozen knowledge, and pluggable tools — and your skill is in what you put into that memory.</p>
</blockquote>
<p>Every technique in the next seven parts is an application of that sentence.</p>
<h2>Exercise</h2>
<p>Ask Claude: <em>"Explain how large language models work, then explain what tokens and context windows are, using an analogy from cooking."</em> Then ask: <em>"What are your own limitations I should watch out for?"</em> — Claude is refreshingly honest about itself.</p>
<h2>Next up</h2>
<p><strong>Part 2:</strong> setting up properly and mastering conversation mechanics — model switching, context hygiene, and the settings most users never find.</p>
]]></content:encoded></item><item><title><![CDATA[Idempotency vs Nonce: Two Weapons Against Duplicate Requests]]></title><description><![CDATA[Every backend developer eventually faces this scenario: a user clicks "Pay" twice, a network timeout causes a client to retry, or a message queue delivers the same event more than once. These situatio]]></description><link>https://blog.abolfazlmohajeri.ir/idempotency-vs-nonce-two-weapons-against-duplicate-requests</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/idempotency-vs-nonce-two-weapons-against-duplicate-requests</guid><category><![CDATA[idempotency]]></category><category><![CDATA[Nonce]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 25 Jun 2026 08:34:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/4f2d38ba-5387-4317-9802-88b405a926e8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every backend developer eventually faces this scenario: a user clicks "Pay" twice, a network timeout causes a client to retry, or a message queue delivers the same event more than once. These situations can create duplicate orders, double charges, or corrupted data.</p>
<p>Two battle-tested strategies exist to defend against this:</p>
<p><strong>Idempotency</strong> and <strong>Nonce</strong>. They look similar on the surface, but they serve different purposes. Let's break them down.</p>
<h2>The Problem: Why Do Duplicates Happen?</h2>
<p>Before diving into solutions, let's understand the enemy.</p>
<pre><code class="language-plaintext">Client  ──── POST /pay ────►  Server
Client  ◄──── timeout ──────  Server (processing...)
Client  ──── POST /pay ────►  Server  ← 💣 DUPLICATE!
</code></pre>
<p>The client never got a response, so it retried — but the first request was already processed. Now you have two charges.</p>
<p>This happens in:</p>
<ul>
<li><p>Mobile apps on unstable connections</p>
</li>
<li><p>Message queues (at-least-once delivery)</p>
</li>
<li><p>Retry logic in service-to-service communication</p>
</li>
<li><p>Frontend double-clicks</p>
</li>
</ul>
<h2>Solution 1: Idempotency</h2>
<h3>What Is It?</h3>
<p>An operation is <strong>idempotent</strong> if calling it once or calling it many times produces the same result.</p>
<blockquote>
<p>f(f(x)) = f(x)</p>
</blockquote>
<p>Think of a light switch that only turns on: pressing it once or ten times — the light is on. Same result.</p>
<p>In HTTP terms: <code>GET</code>, <code>PUT</code>, and <code>DELETE</code> are naturally idempotent. <code>POST</code> is <strong>not</strong> — that's where you need to enforce it yourself.</p>
<h3>How It Works</h3>
<p>The client generates a unique <strong>Idempotency Key</strong> and sends it with every request. The server stores the result of the first execution. On any subsequent request with the same key, the server returns the <strong>cached result</strong> without re-executing the operation.</p>
<pre><code class="language-plaintext">Request 1: POST /pay  {idempotency-key: "abc-123"}  → Processed ✅  (result saved)
Request 2: POST /pay  {idempotency-key: "abc-123"}  → Returned from cache ✅  (not re-executed)
Request 3: POST /pay  {idempotency-key: "abc-123"}  → Returned from cache ✅  (not re-executed)
</code></pre>
<h3>Important Notes</h3>
<ul>
<li><p>The key should be generated by the <strong>client</strong> (usually a UUID v4).</p>
</li>
<li><p>Store idempotency records in a <strong>persistent store</strong> (DB or Redis), not in memory.</p>
</li>
<li><p>Set a <strong>TTL</strong> on records (e.g., 24 hours or 7 days) — you don't need them forever.</p>
</li>
<li><p>Handle <strong>concurrent requests</strong> with the same key using a database unique constraint or distributed lock.</p>
</li>
</ul>
<h2>Solution 2: Nonce</h2>
<h3>What Is It?</h3>
<p><strong>Nonce</strong> stands for <strong>"Number used Once"</strong>. It's a unique token that is valid for <strong>a single use only</strong>. Once consumed, it's permanently invalidated — even if the operation behind it failed.</p>
<p>The key difference from idempotency: a nonce doesn't cache results. It just asks: <em>"Has this token been used before?"</em></p>
<h3>How It Works</h3>
<pre><code class="language-plaintext">Request 1: POST /transfer  {nonce: "xyz-789"}  → Valid, consumed ✅
Request 2: POST /transfer  {nonce: "xyz-789"}  → REJECTED ❌ (already used)
</code></pre>
<p>Even if request 1 failed for some business reason, request 2 with the same nonce is still rejected. The client must generate a <strong>new nonce</strong> for a new attempt.</p>
<h3>Important Notes</h3>
<ul>
<li><p>You can use <strong>Redis</strong> <code>SETNX</code> (SET if Not eXists) for atomic check-and-consume. Never do a separate GET then SET — that's a race condition.</p>
</li>
<li><p>Set a reasonable <strong>TTL</strong> based on your use case (5–15 minutes for form submissions, longer for payment flows).</p>
</li>
<li><p>The client must generate a <strong>new nonce</strong> for every new attempt. Nonces are not reusable.</p>
</li>
<li><p>Nonces work great for <strong>one-time form submissions</strong>, <strong>OTP verification</strong>, and <strong>CSRF protection</strong>.</p>
</li>
</ul>
<h2>Side-by-Side Comparison</h2>
<table>
<thead>
<tr>
<th></th>
<th><strong>Idempotency</strong></th>
<th><strong>Nonce</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>Core question</strong></td>
<td>"Has this operation been done before?"</td>
<td>"Has this token been used before?"</td>
</tr>
<tr>
<td><strong>On duplicate request</strong></td>
<td>Returns cached result</td>
<td>Rejects with error</td>
</tr>
<tr>
<td><strong>Client retry behavior</strong></td>
<td>Safe to retry with same key</td>
<td>Must generate a new nonce</td>
</tr>
<tr>
<td><strong>Stores</strong></td>
<td>Full request result</td>
<td>Just the token</td>
</tr>
<tr>
<td><strong>Best for</strong></td>
<td>Payment APIs, order creation, idempotent POST endpoints</td>
<td>Form submissions, OTPs, CSRF, single-use links</td>
</tr>
<tr>
<td><strong>Response on duplicate</strong></td>
<td><code>200 OK</code> (cached)</td>
<td><code>409 Conflict</code></td>
</tr>
<tr>
<td><strong>Key generated by</strong></td>
<td>Client</td>
<td>Client (or server pre-issues it)</td>
</tr>
<tr>
<td><strong>TTL</strong></td>
<td>Hours to days</td>
<td>Minutes</td>
</tr>
</tbody></table>
<h2>When to Use Which?</h2>
<pre><code class="language-plaintext">Is the client allowed to retry with the same intent?
    │
    ├── YES → Use Idempotency
    │         (same operation, same result expected)
    │         Example: "Charge $100 for order #456"
    │
    └── NO  → Use Nonce
              (one shot only, new attempt = new token)
              Example: "Submit this form", "Verify this OTP"
</code></pre>
<p><strong>Real-world rule of thumb:</strong></p>
<ul>
<li><p>Payment APIs → <strong>Idempotency</strong> (Stripe, PayPal both use this)</p>
</li>
<li><p>Authentication flows, form submissions → <strong>Nonce</strong></p>
</li>
<li><p>CSRF protection → <strong>Nonce</strong></p>
</li>
<li><p>Webhook delivery → <strong>Idempotency</strong></p>
</li>
</ul>
<h2>Combining Both</h2>
<p>In high-security systems, you can use both together. This gives you the best of both worlds: replay protection in the short term (nonce) and safe retries over time (idempotency).</p>
<h2>Summary</h2>
<table>
<thead>
<tr>
<th>Concept</th>
<th>One-liner</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Idempotency</strong></td>
<td>Same key → same result, always safe to retry</td>
</tr>
<tr>
<td><strong>Nonce</strong></td>
<td>Single-use token → rejected on second use</td>
</tr>
</tbody></table>
<p>Both patterns are essential tools in any production backend. Idempotency is your friend when you need fault-tolerant retry logic. Nonce is your guard against replay attacks and accidental double submissions.</p>
<p>Once you start thinking in these terms, you'll spot the need for them everywhere.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding a Codebase with Understand Anything]]></title><description><![CDATA[Every backend developer knows this feeling. You join a new team, clone the repo, and stare at 200,000 lines of code spread across a dozen microservices. The README is half outdated. The person who wro]]></description><link>https://blog.abolfazlmohajeri.ir/understanding-a-codebase-with-understand-anything</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/understanding-a-codebase-with-understand-anything</guid><category><![CDATA[AI]]></category><category><![CDATA[understand-anything]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Fri, 19 Jun 2026 14:34:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/672c44e9-8f49-4e01-ba65-65a220ae456d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every backend developer knows this feeling. You join a new team, clone the repo, and stare at 200,000 lines of code spread across a dozen microservices. The README is half outdated. The person who wrote the auth flow left six months ago. And your first task is to "just add a small feature here."</p>
<p>I've lived this more than once. When everything is connected, the hardest part isn't writing code. It's building a mental model of how the pieces fit together before you dare to touch anything.</p>
<p>Most "code graph" tools don't help here. They render a hairball of nodes and edges and proudly tell you the codebase has 1,400 files. Great — now what?</p>
<h2>A Graph That Teaches</h2>
<p><a href="https://understand-anything.com/">Understand Anything</a> is an open-source plugin that turns any codebase into an interactive knowledge graph you can actually explore and learn from. The pitch that sold me is right there on the homepage: other tools show you structure, this one shows you <em>meaning</em> — how your code maps to real business domains, processes, and flows.</p>
<p>It runs as a plugin for <strong>Claude Code</strong>, but also works with Codex, Cursor, Copilot, Gemini CLI, OpenCode, and a long list of others. Under the hood it analyzes your project with a multi-agent pipeline, builds a graph of every file, function, class, and dependency, then hands you a dashboard to explore it all visually.</p>
<h2>How It Actually Works</h2>
<p>The clever part is the hybrid approach. It doesn't rely purely on an LLM guessing about your code, and it doesn't rely purely on a parser that can't understand intent. It uses both:</p>
<ul>
<li><p><strong>Tree-sitter (deterministic)</strong> parses your source into a syntax tree and extracts the hard structural facts — imports, exports, function and class definitions, call sites, inheritance. Same input, same output, every run. This is what makes the structural graph reproducible.</p>
</li>
<li><p><strong>An LLM (semantic)</strong> then reads that parsed structure alongside the original source to produce what a parser never can — plain-English summaries, architectural layer assignments, and business-domain mapping. The <em>why</em>, not just the <em>what</em>.</p>
</li>
</ul>
<p>That split is the whole point. The structural edges are stable and trustworthy, while the semantic layer captures what a file is actually <em>for</em>.</p>
<p>The <code>/understand</code> command orchestrates a handful of specialized agents — a scanner to discover files and detect frameworks, a file analyzer to extract functions and build nodes and edges, an architecture analyzer to identify layers, a tour builder, and a reviewer to validate the graph. There's also a domain analyzer that extracts business domains, flows, and process steps. File analyzers run in parallel and the whole thing supports incremental updates, so re-running it only re-analyzes the files that changed.</p>
<h2>Get Started in 30 Seconds</h2>
<p>If you're already using Claude Code, installation is two lines:</p>
<pre><code class="language-shell">/plugin marketplace add Egonex-AI/Understand-Anything
/plugin install understand-anything
</code></pre>
<p>Then point it at your project:</p>
<pre><code class="language-shell">/understand
</code></pre>
<p>The pipeline scans everything and saves a knowledge graph to <code>.understand-anything/knowledge-graph.json</code>. Open the dashboard with:</p>
<pre><code class="language-shell">/understand-dashboard
</code></pre>
<p>You get an interactive web view of your codebase as a graph — color-coded by architectural layer, searchable, and clickable. Select any node and you see its code, its relationships, and a plain-English explanation of what it does.</p>
<p>On a non-Claude platform, there's a one-line installer instead:</p>
<pre><code class="language-shell">curl -fsSL https://raw.githubusercontent.com/Egonex-AI/Understand-Anything/main/install.sh | bash
</code></pre>
<h2>The Commands I'd Actually Use Day to Day</h2>
<p>Beyond the initial scan, a few commands map directly onto real situations I run into:</p>
<ul>
<li><p>Ask anything about the codebase</p>
<ul>
<li>/understand-chat How does the payment flow work?</li>
</ul>
</li>
<li><p>See what your current changes will affect — before you commit</p>
<ul>
<li>/understand-diff</li>
</ul>
</li>
<li><p>Deep-dive into one specific file or function</p>
<ul>
<li>/understand-explain src/auth/login.ts</li>
</ul>
</li>
<li><p>Generate an onboarding guide for a new teammate</p>
<ul>
<li>/understand-onboard</li>
</ul>
</li>
<li><p>Extract business domains, flows, and steps</p>
<ul>
<li>/understand-domain</li>
</ul>
</li>
</ul>
<p>That <code>/understand-diff</code> is the one that caught my attention. Knowing the ripple effect of a change before you commit — which services and flows it touches — is exactly the kind of thing that's painful to reason about manually in a tightly coupled microservice setup.</p>
<h2>Sharing the Graph With Your Team</h2>
<p>Here's the detail I appreciated most as someone who's written more than one internal onboarding post. The graph is just JSON, so you can <strong>commit it once and let teammates skip the whole pipeline</strong>. Anyone who clones the repo gets the map for free — ideal for onboarding, PR reviews, and docs-as-code.</p>
<p>You commit everything in <code>.understand-anything/</code> except the local scratch files, and there's an <code>--auto-update</code> flag that installs a post-commit hook so the graph stays in sync with the code on every commit. No more "the diagram is three months out of date."</p>
<h2>Why I Think It's Worth Trying</h2>
<p>The framing on the project sums it up better than I can: the goal isn't a graph that impresses you with how complex your codebase is — it's a graph that quietly teaches you how every piece fits together.</p>
<p>For anyone who regularly inherits unfamiliar code, mentors new hires, or just wants a faster way to build a mental model of a system, this is a genuinely useful tool. It's MIT-licensed and open source, so there's no cost to giving it a spin on your own repo.</p>
<p>If you want to see it before installing anything, there's a <a href="https://understand-anything.com/demo/">live demo</a> you can pan, zoom, and search right in the browser. Otherwise, point it at your messiest service and see what it teaches you.</p>
<hr />
<p><em>Have you tried it on a large codebase? I'd be curious how it holds up on a multi-service setup — let me know in the comments.</em></p>
]]></content:encoded></item><item><title><![CDATA[Redisson Starter]]></title><description><![CDATA[Redisson is a feature-rich Java client for Redis, designed to offer high-level abstractions and easy integration with Java applications.
Why do we choose redisson?
While multiple Redis clients are ava]]></description><link>https://blog.abolfazlmohajeri.ir/redisson-starter</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/redisson-starter</guid><category><![CDATA[Redis]]></category><category><![CDATA[redisson]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 11 Dec 2025 09:21:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/913adaf7-a562-48b3-b4bf-cfe8bcfa2494.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Redisson is a feature-rich Java client for Redis, designed to offer high-level abstractions and easy integration with Java applications.</p>
<h1><strong>Why do we choose redisson?</strong></h1>
<p>While multiple Redis clients are available for Java, including Jedis, Lettuce, and Spring Data Redis, Redisson stands out due to its rich feature set, ease of use, and strong support for distributed systems. <strong>New:</strong> Redisson now supports valkey.</p>
<h2><strong>Clients Comparison</strong></h2>
<p>Below is a quick comparison of the most popular Java Redis clients:</p>
<table>
<thead>
<tr>
<th><strong>Feature/Client</strong></th>
<th><strong>Redisson</strong></th>
<th><strong>Spring Data Redis</strong></th>
<th><strong>Jedis</strong></th>
<th><strong>Lettuce</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>Distributed Objects</strong></td>
<td>Rich set (Map, Set, List, Lock, Semaphore, etc.)</td>
<td>Not available</td>
<td>Not available</td>
<td>Not available</td>
</tr>
<tr>
<td><strong>Distributed Locks</strong></td>
<td>Built-in and easy to use</td>
<td>Needs manual implementation or extensions</td>
<td>No built-in support</td>
<td>No built-in support</td>
</tr>
<tr>
<td><strong>Serialization</strong></td>
<td>Customizable, supports many codecs</td>
<td>Limited to RedisTemplate configuration</td>
<td>Manual serialization needed</td>
<td>Limited</td>
</tr>
<tr>
<td><strong>Spring Boot Integration</strong></td>
<td>Dedicated starter and auto-configuration</td>
<td>Native support</td>
<td>Not provided out of the box</td>
<td>Not supplied out of the box</td>
</tr>
<tr>
<td><strong>Thread Safety</strong></td>
<td>Fully thread-safe</td>
<td>Depends on the underlying client</td>
<td>Not thread-safe</td>
<td>Thread-safe</td>
</tr>
</tbody></table>
<p>Based on this table and references, <strong>Redisson</strong> is a great choice.</p>
<h2><strong>References</strong></h2>
<ul>
<li><p><a href="https://redisson.pro/blog/feature-comparison-redisson-vs-spring-data-redis.html">Feature Comparison: Redisson vs Spring Data Redis</a></p>
</li>
<li><p><a href="https://redisson.pro/blog/feature-comparison-redisson-vs-lettuce.html">Feature Comparison: Redisson vs Lettuce</a></p>
</li>
<li><p><a href="https://redisson.pro/blog/feature-comparison-redisson-vs-jedis.html">Feature Comparison: Redisson vs Jedis</a></p>
</li>
</ul>
<h1><strong>Spring Boot Starter</strong></h1>
<p>In this starter, you'll learn how to use Redis with the Redisson library. All examples use Spring Boot 3.4.5 with Redisson 3.45.1.</p>
<p><strong>ATTENTION:</strong> You can see full examples <a href="https://github.com/abmohajeri/spring-boot-redisson-best-practices">in this GitHub repo.</a></p>
<p>To get started, include the following dependency:</p>
<pre><code class="language-xml">&lt;dependency&gt;
   &lt;groupId&gt;org.redisson&lt;/groupId&gt;
   &lt;artifactId&gt;redisson-spring-boot-starter&lt;/artifactId&gt;
   &lt;version&gt;3.45.1&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<p>While you can also use the redisson artifact directly, using the redisson-spring-boot-starter is recommended. It provides useful Spring Boot auto-configurations, health checks through Actuator, and additional Spring-specific integrations.</p>
<p>We will configure the Redis cluster based on the <a href="https://docs.google.com/document/d/1FNRLst9TojaTawPHwUWq8QZ5SyJp88ij49VNGGJ_flg/edit?tab=t.0#heading=h.tyzu9j8tve83">doc we provided before</a>.</p>
<pre><code class="language-yaml">spring:
  redis:
    redisson:
      file: classpath:redisson.yaml
</code></pre>
<p>In redisson.yaml:</p>
<pre><code class="language-yaml">clusterServersConfig:
</code></pre>
<p>There are a lot more important configs that you can set in this file that you can see in <strong>Appendix 1</strong>. Tuning these configs is based on your needs.</p>
<h2><strong>Important Notes</strong></h2>
<ul>
<li><p>Connection Pool Size</p>
<ul>
<li>Ensure that the connection pool size is properly configured based on your workload. A pool that's too small can lead to connection timeouts under high load, while an oversized pool may waste resources.</li>
</ul>
</li>
<li><p>Cluster Scan Frequency</p>
<ul>
<li>Avoid setting the scanInterval too low. Scanning the cluster too frequently can create unnecessary network overhead and impact performance, especially in large-scale deployments.</li>
</ul>
</li>
<li><p>Ping Interval Configuration</p>
<ul>
<li>Frequent pings to maintain connections can lead to excessive traffic and latency. Tune the pingConnectionInterval based on your environment and latency tolerance.</li>
</ul>
</li>
</ul>
<h2><strong>References</strong></h2>
<ul>
<li><a href="https://redisson.pro/docs/configuration/">Redisson Configuration</a></li>
</ul>
<h1><strong>Redis Health for Spring Boot App</strong></h1>
<p>Spring Boot Actuator provides a default Redis health indicator, but it offers very limited information. By default, it returns something like:</p>
<pre><code class="language-json">"redis": {
    "status": "UP",
    "details": {
        "cluster_size": 3,
        "slots_up": 16384,
        "slots_fail": 0
    }
}
</code></pre>
<p>To get more detailed insights—such as the health status of Redis slave nodes—you can implement a custom health indicator based on your needs. For example:</p>
<pre><code class="language-java">@Slf4j
@Component
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
public class RedisMastersHealthIndicator implements HealthIndicator {
    RedissonClient redissonClient;

    @Override
    public Health health() {
        RedisCluster cluster = redissonClient.getRedisNodes(RedisNodes.CLUSTER);
        Collection&lt;RedisClusterMaster&gt; masters = cluster.getMasters();
        for (RedisClusterMaster master : masters) {
            try {
                if (!master.ping()) {
                    log.error("Redis slave node {} is DOWN", master);
                    return Health.down().withDetail("failedNode", master.toString()).build();
                }
            } catch (Exception e) {
                log.error("Redis slave node {} is DOWN", master, e);
                return Health.down().withDetail("failedNode", master.toString()).build();
            }
        }
        return Health.up().withDetail("mastersChecked", masters.size()).build();
    }
}
</code></pre>
<p>This custom indicator checks the health of Redis slave nodes and reports if any of them are unreachable.</p>
<h1><strong>Distributed Collections (Data Structures)</strong></h1>
<p>These are <a href="https://docs.google.com/document/d/19-HAWELkk_kurIHq9Lr9K-DqtgmBew-kg6R4i7Ytvzk/edit?tab=t.0#heading=h.tyzu9j8tve83">redis data structures</a> with redisson wrapper:</p>
<table>
<thead>
<tr>
<th><strong>Redis Type</strong></th>
<th><strong>Description</strong></th>
<th><strong>Redisson Equivalent</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>String</strong></td>
<td><strong>Basic key-value store.</strong></td>
<td><strong>RBucket</strong></td>
</tr>
<tr>
<td><strong>List</strong></td>
<td><strong>Ordered list of values.</strong></td>
<td><strong>RList</strong></td>
</tr>
<tr>
<td><strong>Set</strong></td>
<td><strong>Unordered unique values.</strong></td>
<td><strong>RSet</strong></td>
</tr>
<tr>
<td><strong>Hash</strong></td>
<td><strong>Key-value pairs within a key.</strong></td>
<td><strong>RMap</strong></td>
</tr>
<tr>
<td><strong>Sorted Set</strong></td>
<td><strong>Sorted by score.</strong></td>
<td><strong>RSortedSet</strong></td>
</tr>
<tr>
<td><strong>Geo</strong></td>
<td><strong>Geospatial indexes.</strong></td>
<td><strong>RGeo</strong></td>
</tr>
<tr>
<td><strong>Bitmaps</strong></td>
<td><strong>Bit-level operations on strings.</strong></td>
<td><strong>Not mapped, can use RBitSet</strong></td>
</tr>
</tbody></table>
<p>Example of each:</p>
<pre><code class="language-java">RBucket&lt;String&gt; bucket = redissonClient.getBucket("myKey");
bucket.set("Hello, Redisson!");
System.out.println(bucket.get());

RList&lt;String&gt; list = redissonClient.getList("myList");
list.add("Apple");
list.add("Banana");
System.out.println(list.get(0));

RSet&lt;String&gt; set = redissonClient.getSet("mySet");
set.add("A");
set.add("B");
set.add("A");
System.out.println(set.contains("B"));

RMap&lt;String, Integer&gt; map = redissonClient.getMap("myMap");
map.put("views", 10);
System.out.println(map.get("views"));

RScoredSortedSet&lt;String&gt; scoredSet = redissonClient.getScoredSortedSet("mySortedSet");
scoredSet.add(9.5, "Alice");
scoredSet.add(8.0, "Bob");
System.out.println(scoredSet.first());

RGeo&lt;String&gt; geo = redissonClient.getGeo("myGeo");
geo.add(13.361389, 38.115556, "Palermo");
geo.add(15.087269, 37.502669, "Catania");
System.out.println(geo.dist("Palermo", "Catania", GeoUnit.KILOMETERS));

RBitSet bitSet = redissonClient.getBitSet("myBitSet");
bitSet.set(0, true);
bitSet.set(1, false);
System.out.println(bitSet.get(0));
</code></pre>
<h3><strong>Map &amp; Set</strong></h3>
<p>Redisson provides various Map structure implementations with multiple important features (lots of them are only for redisson pro 🙁): (In <strong>appendix 2</strong>, you can read about the meaning of local cache and data partitioning)</p>
<ol>
<li><p>No eviction</p>
<ol>
<li><p>Available implementations:</p>
<ol>
<li><p>getMap()</p>
</li>
<li><p>getLocalCachedMap();</p>
</li>
</ol>
</li>
</ol>
</li>
<li><p>Scripted eviction</p>
<ol>
<li><p>Allows for defining time to live or max idle time parameters per map entry.</p>
</li>
<li><p>Eviction is done on the redisson side through a custom-scheduled task that removes expired entries using La ua script.</p>
</li>
<li><p>Available implementations:</p>
<ol>
<li><p>getMapCache()</p>
<ol>
<li>map.putIfAbsent("key2", new SomeObject(), 40, TimeUnit.SECONDS, 10, TimeUnit.SECONDS);</li>
</ol>
</li>
</ol>
</li>
</ol>
</li>
<li><p>Advanced eviction</p>
<ol>
<li>All for Redisson Pro</li>
</ol>
</li>
<li><p>Native eviction</p>
<ol>
<li><p>Allows for defining time-to-live parameters per map entry.</p>
</li>
<li><p>Doesn't use an entry eviction task, entries are cleaned on Redis side.</p>
</li>
<li><p><strong>Requires Redis 7.4+.</strong></p>
</li>
<li><p>Available implementations:</p>
<ol>
<li>getMapCacheNative()</li>
</ol>
</li>
</ol>
</li>
</ol>
<p>Similar methods are available for RSet as well. For more details, refer to the <a href="https://redisson.pro/docs/data-and-services/collections/">documentation</a>.</p>
<p><strong>Danger:</strong> Don't Rely on Redisson TTL Alone. If TTL is critical to your business logic (e.g. session expiry, rate limiting, locks):</p>
<ul>
<li><p>Always use Redis-native expiration.</p>
</li>
<li><p>Avoid relying on Redisson TTL if app uptime isn't guaranteed.</p>
</li>
</ul>
<p>Solutions may:</p>
<ul>
<li><p>RBucket and RMapCacheNative support redis ttl</p>
</li>
<li><p>Sometimes you can use the Redis key TTL instead of per-entry TTL.</p>
<ul>
<li>redissonClient.getKeys().expire("myMap", 10, TimeUnit.SECONDS);</li>
</ul>
</li>
</ul>
<h3><strong>Multimap</strong></h3>
<p>Multimap for Java allows binding multiple values per key. This object is thread-safe. Keys are limited to 4,294,967,295 elements.</p>
<p>Multimap distributed object for Java with eviction support implemented by separated MultimapCache object. There are RSetMultimapCache and RListMultimapCache objects for Set and List based Multimaps respectively. Eviction task is started once per unique object name at the moment of getting a Multimap instance.</p>
<pre><code class="language-java">RSetMultimap&lt;Integer, Integer&gt; set = redissonClient.getSetMultimap("mySetMultimap");
set.put(1, 1);
set.put(1, 2);
set.put(1, 3);
set.put(1, 3);
set.put(2, 1);
System.out.println("SetMultimap:");
set.keySet().forEach(key -&gt; {
    System.out.println(key + " =&gt; " + set.get(key));
});

RListMultimap&lt;Integer, Integer&gt; map = redissonClient.getListMultimap("myListMultimap");
map.put(1, 1);
map.put(1, 2);
map.put(1, 3);
map.put(1, 3);
map.put(2, 1);
System.out.println("ListMultimap:");
map.keySet().forEach(key -&gt; {
    System.out.println(key + " =&gt; " + map.get(key));
});
</code></pre>
<p><strong>Danger:</strong> Redisson's RSetMultimap and RListMultimap can create many Redis keys, especially when you have many keys or values.</p>
<p>When you use:</p>
<pre><code class="language-java">RSetMultimap&lt;Integer, Integer&gt; map = redissonClient.getSetMultimap("mySetMultimap");
</code></pre>
<p>Redisson internally creates one Redis key for each map entry (and some metadata), like:</p>
<pre><code class="language-java">mySetMultimap:{key}

mySetMultimap, mySetMultimap:{1}, mySetMultimap:{2}, and …
</code></pre>
<p>This causes many problems, such as key growth if there are many keys/values, Slower operations for listing or scanning, and …</p>
<p>These are some solution for this:</p>
<ul>
<li><p>Use RMap&lt;K, Set&gt; instead of Redisson Multimap.</p>
</li>
<li><p>Distribute keys using modulo: multimap:.</p>
</li>
<li><p>Use RMap&lt;String, String&gt; with CSV/JSON values (Not Recommend)</p>
</li>
</ul>
<h2><strong>Important Notes</strong></h2>
<ul>
<li><p>Redisson allows you to bind listeners to certain collections, such as RMap. For more details, refer to the <a href="https://redisson.pro/docs/data-and-services/collections/">documentation</a>.</p>
</li>
<li><p>Redisson also supports <a href="https://redisson.pro/docs/data-and-services/queues/">distributed queues</a>.</p>
</li>
<li><p>Redisson also supports <a href="https://redisson.pro/docs/data-and-services/collections/#time-series">time series</a>.</p>
</li>
<li><p>For some data structures choosing a good codec will save a lot of cost.</p>
</li>
</ul>
<h2><strong>References</strong></h2>
<ul>
<li><p><a href="https://redisson.pro/docs/data-and-services/objects/">Distributed Objects</a></p>
</li>
<li><p><a href="https://redisson.pro/docs/data-and-services/collections/">Distributed Collections</a></p>
</li>
</ul>
<h1><strong>Distributed Locks</strong></h1>
<p>Redisson provides robust distributed locking mechanisms to ensure safe concurrent access in clustered or multi-pod environments.</p>
<p>You can acquire a lock using the following methods:</p>
<ul>
<li><p>RLock.lock()</p>
<ul>
<li><p>Blocks indefinitely until the lock is acquired.</p>
</li>
<li><p>Simple, but use it only when you're sure the lock will be released eventually.</p>
</li>
</ul>
</li>
<li><p>RLock.tryLock(waitTime, leaseTime, timeUnit)</p>
<ul>
<li><p>Attempts to acquire the lock:</p>
<ul>
<li><p>Waits up to waitTime.</p>
</li>
<li><p>If acquired, holds for lease time and then auto-releases.</p>
</li>
</ul>
</li>
<li><p>Safer than lock() in most distributed environments.</p>
</li>
</ul>
</li>
<li><p>RReadWriteLock</p>
<ul>
<li><p>Allows many readers OR one writer.</p>
</li>
<li><p>Useful when:</p>
<ul>
<li><p>Multiple pods need concurrent read access.</p>
</li>
<li><p>Only one pod should perform write/update at a time.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>RFairLock</p>
<ul>
<li><p>Ensures first-come, first-served access to the lock.</p>
</li>
<li><p>Use when fairness (e.g., request order) is important.</p>
</li>
</ul>
</li>
<li><p>RMultiLock</p>
<ul>
<li><p>Combines multiple locks into a single logical lock.</p>
</li>
<li><p>Acquires all or none. Ideal when working with multiple related resources (e.g., account transfers).</p>
</li>
</ul>
</li>
</ul>
<h2><strong>Important Notes</strong></h2>
<ul>
<li><p>Lock key should be unique per logical task (e.g. "lock:register:" + userId).</p>
</li>
<li><p>Always release locks in finally blocks to prevent deadlocks.</p>
</li>
<li><p>Avoid overusing locks (they add latency and reduce throughput)</p>
<ul>
<li><p>Use locks only when multiple related keys or complex logic must be executed as a unit.</p>
</li>
<li><p>Favor atomic Redis operations when possible.</p>
<ul>
<li><p>Atomic operations like INCR, SETNX, and HINCRBY are built-in, single-step commands in Redis.</p>
</li>
<li><p>They avoid the overhead of acquiring/releasing distributed locks.</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>By default lock watchdog timeout is 30 seconds and can be changed through Config.lockWatchdogTimeout setting.</p>
</li>
<li><p>Read full locks <a href="https://redisson.pro/docs/data-and-services/locks-and-synchronizers">here</a> based on your needs.</p>
</li>
</ul>
<h2><strong>References</strong></h2>
<ul>
<li><a href="https://redisson.pro/docs/data-and-services/locks-and-synchronizers/">Distributed locks and synchronizers</a></li>
</ul>
<h1><strong>Distributed Tasks</strong></h1>
<p>Redisson supports distributed task execution using the RExecutorService, allowing tasks to be submitted and executed across multiple nodes in a cluster.</p>
<p>Here's a compact summary of the most useful Redisson Distributed Task methods:</p>
<ul>
<li><p>submit(task)</p>
<ul>
<li>Submits a task for execution on any available node.</li>
</ul>
</li>
<li><p>schedule(task, delay, unit)</p>
<ul>
<li>Schedules a one-time task to run after a delay.</li>
</ul>
</li>
</ul>
<h2><strong>Important Notes</strong></h2>
<ul>
<li><p>registerWorkers is crucial when using Redisson's RScheduledExecutorService or RExecutorService.</p>
<ul>
<li><p>It registers the local JVM process as a worker node that can execute submitted tasks.</p>
</li>
<li><p>executor.registerWorkers(...) tells Redisson: “This instance is available to run background tasks.”</p>
</li>
<li><p>WorkerOptions.defaults().workers(1) means: “Allow 1 background thread in this instance to execute submitted jobs.”</p>
</li>
</ul>
</li>
<li><p>You can add listeners like TaskSuccessListener to options.</p>
</li>
<li><p>You can add runnable and callable.</p>
</li>
<li><p>You can use cron in the schedule method.</p>
</li>
</ul>
<h2><strong>References</strong></h2>
<ul>
<li><a href="https://redisson.pro/docs/data-and-services/services/#executor-service">Executor service</a></li>
</ul>
<h1><strong>Pub/Sub</strong></h1>
<p>Java RTopic object implements Publish / Subscribe mechanism based on Redis Pub/Sub. It allows clients to subscribe to events published with multiple instances of RTopic objects with the same name. Listeners are re-subscribed automatically after reconnection or failover.</p>
<p>If you're not using Reliable Topic, any messages published while the client is disconnected will be lost. Use RReliableTopic if message durability is critical.</p>
<h2><strong>References</strong></h2>
<ul>
<li><a href="https://redisson.pro/docs/data-and-services/publish-subscribe/">Distributed publish/subscribe</a></li>
</ul>
<h1><strong>Streams</strong></h1>
<p>Redisson Streams provides a Redis-based implementation of the Redis Streams data structure, which is ideal for building scalable, event-driven systems.</p>
<h2><strong>Important Notes</strong></h2>
<ul>
<li><p>Always ack() messages after successful processing to remove them from the pending list.</p>
</li>
<li><p>Redisson has more methods like pending and claim that you can use based on your needs.</p>
</li>
</ul>
<h2><strong>References</strong></h2>
<ul>
<li><a href="https://redisson.pro/docs/data-and-services/queues/#stream">Stream</a></li>
</ul>
<h1><strong>General Important Notes</strong></h1>
<p>Before using Redis in your service, please review and follow these important guidelines:</p>
<ol>
<li><p>**Evaluate if Redis is the right choice:<br />**Make sure Redis fits your architecture and use case. Redis is best suited for high-performance caching, real-time analytics, pub/sub messaging, distributed locks, and short-lived data. It is <strong>not</strong> ideal for storing large or critical persistent data unless configured with proper persistence and backup strategies.</p>
</li>
<li><p>**Design for fault tolerance:<br />**Your service must handle Redis failures gracefully. Redis is an external dependency and can become temporarily unavailable.</p>
<ul>
<li><p>Always implement fallback mechanisms or default behaviors when Redis is down.</p>
</li>
<li><p>Avoid making your entire service dependent on Redis availability.</p>
</li>
</ul>
</li>
<li><p>**Be careful with data eviction and TTLs:<br />**If you use Redis as a cache, always define appropriate TTL values to prevent stale data and memory exhaustion.</p>
<ul>
<li>Do not assume Redis data will persist indefinitely.</li>
</ul>
</li>
<li><p>**Use proper serialization and data structures:<br />**Choose an efficient serialization format and be consistent across services.<br />Use the right Redis data type for your purpose (String, Hash, List, Set, Sorted Set, etc.)</p>
</li>
<li><p>**Monitor and observe Redis usage:<br />**Continuously monitor key metrics like memory usage, hit/miss ratio, latency, and connection counts.<br />Tools like RedisInsight and Grafana metrics can help.</p>
</li>
</ol>
<h1><strong>Helpful Definitions</strong></h1>
<table>
<thead>
<tr>
<th><strong>Term</strong></th>
<th><strong>Meaning</strong></th>
</tr>
</thead>
<tbody><tr>
<td>High Availability</td>
<td>The system keeps working even if some parts stop</td>
</tr>
<tr>
<td>Fault Tolerance</td>
<td>The ability of a system to continue working even if some parts fail. It prevents total system failure</td>
</tr>
<tr>
<td>Failover</td>
<td>Switching to a backup system if the main one fails</td>
</tr>
<tr>
<td>Replication</td>
<td>Making copies of data from one server to another</td>
</tr>
<tr>
<td>Throughput</td>
<td>The amount of work or data a system can process in a given amount of time.</td>
</tr>
<tr>
<td>Latency</td>
<td>The time it takes for a request to travel from sender to receiver and get a response.</td>
</tr>
<tr>
<td>Scalability</td>
<td>The system’s ability to handle increased load by adding resources.</td>
</tr>
</tbody></table>
<h1><strong>Appendix</strong></h1>
<h2><strong>Appendix 1</strong></h2>
<p>Most important configs are:</p>
<p><strong>- clientName:</strong></p>
<ul>
<li><p>Default value: null</p>
</li>
<li><p>Name of client connection.</p>
</li>
</ul>
<p><strong>- nodeAddresses:</strong></p>
<ul>
<li>Redisson automatically discovers the cluster topology.</li>
</ul>
<p><strong>- readMode:</strong></p>
<ul>
<li><p>Default value: SLAVE</p>
</li>
<li><p>Set node type used for read operation.</p>
</li>
<li><p>Available values: SLAVE, MASTER and MASTER_SLAVE</p>
</li>
</ul>
<p><strong>- subscriptionMode:</strong></p>
<ul>
<li><p>Default value: MASTER</p>
</li>
<li><p>Set node type used for pub/sub operation.</p>
</li>
<li><p>Available values: SLAVE and MASTER</p>
</li>
</ul>
<p><strong>- scanInterval:</strong></p>
<ul>
<li><p>Default value: 1000</p>
</li>
<li><p>Applied clusters topology scans.</p>
</li>
</ul>
<p><strong>- pingConnectionInterval:</strong></p>
<ul>
<li><p>Default value: 30000</p>
</li>
<li><p>This setting allows for detecting and reconnecting broken connections, using the PING command.</p>
</li>
<li><p>Set to 0 to disable.</p>
</li>
</ul>
<p><strong>- slave/master/subscriptionConnectionMinimumIdleSize:</strong></p>
<ul>
<li><p>Default value: 24</p>
</li>
<li><p>Minimum idle connections amount per node/channels.</p>
</li>
</ul>
<p><strong>- slave/master/subscriptionConnectionPoolSize:</strong></p>
<ul>
<li><p>Default value: 64</p>
</li>
<li><p>Maximum connection pool size per node/channels.</p>
</li>
</ul>
<p><strong>- connectTimeout:</strong></p>
<ul>
<li><p>Default value: 10000</p>
</li>
<li><p>Timeout in milliseconds during connecting to server.</p>
</li>
</ul>
<p><strong>- idleConnectionTimeout:</strong></p>
<ul>
<li><p>Default value: 10000</p>
</li>
<li><p>If a pooled connection is not used for a timeout time and the current connections amount is bigger than the minimum idle connections pool size, then it will be closed and removed from the pool.</p>
</li>
</ul>
<p><strong>- subscriptionTimeout:</strong></p>
<ul>
<li><p>Default value: 7500</p>
</li>
<li><p>Defines subscription timeout in milliseconds applied per channel subscription.</p>
</li>
</ul>
<p><strong>- timeout:</strong></p>
<ul>
<li><p>Default value: 3000</p>
</li>
<li><p>Server response timeout in milliseconds.</p>
</li>
<li><p>Starts countdown after a command is successfully sent.</p>
</li>
</ul>
<p><strong>- retryAttempts:</strong></p>
<ul>
<li><p>Default value: 3</p>
</li>
<li><p>Error will be thrown if command can’t be sent to server after retryAttempts. But if it is sent successfully then timeout will be started.</p>
</li>
</ul>
<p><strong>- retryInterval:</strong></p>
<ul>
<li><p>Default value: 1500</p>
</li>
<li><p>Time interval in milliseconds, after which another attempt to send a command will be executed.</p>
</li>
</ul>
<p><strong>- failedSlaveReconnectionInterval:</strong></p>
<ul>
<li><p>Default value: 3000</p>
</li>
<li><p>Interval of Slave reconnection attempts, when it was excluded from an internal list of available servers.</p>
</li>
<li><p>On each timeout event, Redisson tries to connect to the disconnected server.</p>
</li>
</ul>
<p><strong>- failedSlaveNodeDetector:</strong></p>
<ul>
<li><p>Default value: org.redisson.client.FailedConnectionDetector</p>
</li>
<li><p>Defines the failed Slave node detector object which implements failed node detection logic via the org.redisson.client.FailedNodeDetector interface.</p>
</li>
<li><p>Available implementations:</p>
<ul>
<li><p>org.redisson.client.FailedConnectionDetector</p>
</li>
<li><p>org.redisson.client.FailedCommandsDetector</p>
</li>
<li><p>org.redisson.client.FailedCommandsTimeoutDetector</p>
</li>
</ul>
</li>
</ul>
<p><strong>- keepAlive:</strong></p>
<ul>
<li><p>Default value: false</p>
</li>
<li><p>Enables TCP keepAlive for connection.</p>
</li>
</ul>
<p><strong>- tcpNoDelay:</strong></p>
<ul>
<li><p>Default value: true</p>
</li>
<li><p>Enables TCP noDelay for connections.</p>
</li>
</ul>
<p><strong>- nettyThreads:</strong></p>
<ul>
<li><p>Default value: 32</p>
</li>
<li><p>Defines the number of threads shared between all internal clients used by Redisson.</p>
</li>
<li><p>Netty threads are used for response decoding and command sending.</p>
</li>
<li><p>0 = cores_amount * 2</p>
</li>
</ul>
<p><strong>- threads:</strong></p>
<ul>
<li><p>Default value: 16</p>
</li>
<li><p>Threads are used to execute the listener's logic of the RTopic object, invocation handlers of the RRemoteService, the RTopic object and RExecutorService tasks.</p>
</li>
</ul>
<p><strong>- codec:</strong></p>
<ul>
<li><p>Default value: org.redisson.codec.Kryo5Codec</p>
</li>
<li><p>Used during read/write data operations.</p>
</li>
<li><p>Several serialization <a href="https://redisson.pro/docs/data-and-services/data-serialization/">implementations</a> are available.</p>
</li>
</ul>
<p><strong>- lockWatchdogTimeout:</strong></p>
<ul>
<li><p>Default value: 30000</p>
</li>
<li><p>This prevents infinity-locked locks.</p>
</li>
<li><p>This parameter is only used if an RLock object is acquired without the leaseTimeout parameter.</p>
</li>
</ul>
<h2><strong>Appendix 2</strong></h2>
<p>Here is a meaning of local cache and data partitioning:</p>
<ul>
<li><p>Local cache</p>
<ul>
<li>So called near cache used to speed up read operations and avoid network round trips. It caches Map entries on Redisson side and executes read operations up to 45x faster in comparison with common implementation.</li>
</ul>
</li>
<li><p>Data partitioning</p>
<ul>
<li>Although any Map object is cluster compatible its content isn't scaled/partitioned across multiple master nodes in the cluster. Data partitioning allows to scale available memory, read/write operations and entry eviction processes for individual Map instances in a cluster.</li>
</ul>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Redis Data Structures]]></title><description><![CDATA[In this article, you can find the most useful Redis data structures with brief details for each.
To see all data structures, including extension data types, visit the link below:Redis Data Types Docum]]></description><link>https://blog.abolfazlmohajeri.ir/redis-data-structures</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/redis-data-structures</guid><category><![CDATA[Redis]]></category><category><![CDATA[redis data structure]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 11 Dec 2025 08:45:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/0b853cf2-a3b3-4db4-b302-6a31135163da.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, you can find the most useful Redis data structures with brief details for each.</p>
<p>To see all data structures, including extension data types, visit the link below:<br /><a href="https://redis.io/docs/latest/develop/data-types">Redis Data Types Documentation</a> + <a href="https://redis.io/tutorials/howtos/quick-start/cheat-sheet">Cheat Sheets</a></p>
<p>The link below provides the time complexity for all Redis commands, which is very helpful in selecting the right data structure for your use case:<br /><a href="https://github.com/ZhenningLang/redis-command-complexity-cheatsheet">Redis Command Time Complexity Cheat Sheet</a></p>
<h2><strong>Strings</strong></h2>
<p>Redis strings store sequences of bytes, including text, serialized objects, and binary arrays.</p>
<h3>Example</h3>
<pre><code class="language-shell">&gt; SET bike:1 Deimos
OK
&gt; GET bike:1
"Deimos"
</code></pre>
<h3>Usage</h3>
<ul>
<li><p>Storing simple key-value pairs</p>
</li>
<li><p>Caching responses</p>
</li>
<li><p>Counting (using INCR, DECR)</p>
</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li><p>Most string operations are O(1)</p>
</li>
<li><p>Be careful with the SUBSTR, GETRANGE, and SETRANGE commands, which can be O(n)</p>
</li>
<li><p>Redis string can be a maximum of 512 MB (Danger)</p>
</li>
</ul>
<h3>Helpful Links</h3>
<ul>
<li><p><a href="https://redis.io/docs/latest/develop/data-types/strings">https://redis.io/docs/latest/develop/data-types/strings</a></p>
</li>
<li><p><a href="https://redis.io/docs/latest/commands/?group=string">https://redis.io/docs/latest/commands/?group=string</a></p>
</li>
</ul>
<h2><strong>Lists</strong></h2>
<p>Redis lists are ordered collections of strings, implemented as linked lists.</p>
<h3>Example</h3>
<pre><code class="language-shell">&gt; RPUSH todo:123 "Buy milk"
&gt; RPUSH todo:123 "Walk the dog"
&gt; LPUSH todo:123 "Check emails"
&gt; LRANGE todo:123 0 -1
1) "Check emails"
2) "Buy milk"
3) "Walk the dog"
</code></pre>
<h3>Usage</h3>
<ul>
<li><p>Implementing queues (FIFO, LIFO)</p>
</li>
<li><p>Twitter social network takes the latest tweets posted by users into Redis lists - <a href="https://www.infoq.com/presentations/Real-Time-Delivery-Twitter/">reference</a></p>
</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li><p>Operations that access its head or tail are O(1)</p>
</li>
<li><p>Commands that manipulate elements within a list are usually O(n) (Danger)</p>
<ul>
<li>Like LINDEX, LINSERT, and LSET</li>
</ul>
</li>
<li><p>Redis streams is an alternative to lists when you need to store and process an indeterminate series of events</p>
</li>
</ul>
<h3>Helpful Links</h3>
<ul>
<li><p><a href="https://redis.io/docs/latest/develop/data-types/lists">https://redis.io/docs/latest/develop/data-types/lists</a></p>
</li>
<li><p><a href="https://redis.io/docs/latest/commands/?group=list">https://redis.io/docs/latest/commands/?group=list</a></p>
</li>
</ul>
<h2><strong>Sets</strong></h2>
<p>Redis Set is an unordered collection of unique strings.</p>
<h3>Example</h3>
<pre><code class="language-shell">&gt; SADD users:online Alice
&gt; SADD users:online Bob
&gt; SADD users:online Alice
&gt; SMEMBERS users:online
1) "Alice"
2) "Bob"
</code></pre>
<h3>Usage</h3>
<ul>
<li><p>Track unique items (e.g., track all unique IP addresses accessing a given blog post).</p>
</li>
<li><p>Represent relations (e.g., the set of all users with a given role).</p>
</li>
<li><p>Perform common set operations such as intersection, unions, and differences.</p>
</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li><p>Most set operations, including adding, removing, and checking whether an item is a set member, are O(1)</p>
</li>
<li><p>SMEMBERS command is O(n) and returns the entire set in a single response</p>
</li>
</ul>
<h3>Helpful Links</h3>
<ul>
<li><p><a href="https://redis.io/docs/latest/develop/data-types/sets">https://redis.io/docs/latest/develop/data-types/sets</a></p>
</li>
<li><p><a href="https://redis.io/docs/latest/commands/?group=set">https://redis.io/docs/latest/commands/?group=set</a></p>
</li>
</ul>
<h2><strong>Hashes</strong></h2>
<p>Redis hashes are key-value maps, useful for storing objects.</p>
<h3>Example</h3>
<pre><code class="language-shell">&gt; HSET user:100 name "Alice" age "30"
&gt; HGET user:100 name
"Alice"
&gt; HGETALL user:100
1) "name"
2) "Alice"
3) "age"
4) "30"
</code></pre>
<h3>Usage</h3>
<ul>
<li>Caching database rows and …</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li><p>New in Redis Community Edition 7.4 is the ability to specify an expiration time or a time-to-live (TTL) value for individual hash fields.</p>
</li>
<li><p>Most Redis hash commands are O(1).</p>
</li>
<li><p>A few commands and most of the expiration-related commands are O(n) (Danger)</p>
<ul>
<li>Like HKEYS, HVALS, HGETALL</li>
</ul>
</li>
<li><p>The number of fields in a hash is limited only by the total memory available on the Redis server.</p>
</li>
</ul>
<h3>Helpful Links</h3>
<ul>
<li><p><a href="https://redis.io/docs/latest/develop/data-types/hashes">https://redis.io/docs/latest/develop/data-types/hashes</a></p>
</li>
<li><p><a href="https://redis.io/docs/latest/commands/?group=hash">https://redis.io/docs/latest/commands/?group=hash</a></p>
</li>
</ul>
<h2><strong>Sorted Sets (Zsets)</strong></h2>
<p>Zsets store unique strings with an associated score for ordering.</p>
<h3>Example</h3>
<pre><code class="language-shell">&gt; ZADD leaderboard 100 "Alice"
&gt; ZADD leaderboard 200 "Bob"
&gt; ZADD leaderboard 150 "Charlie"
&gt; ZRANGE leaderboard 0 -1 WITHSCORES
1) "Alice"
2) "100"
3) "Charlie"
4) "150"
5) "Bob"
6) "200"
</code></pre>
<h3>Usage</h3>
<ul>
<li><p>Leaderboards</p>
</li>
<li><p>Rate limiters</p>
</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li><p>Most sorted set operations are O(log(n))</p>
</li>
<li><p>ZRANGE command's time complexity is O(log(n) + m), where m is the number of results returned.</p>
</li>
<li><p>You can think of sorted sets as a mix between a Set and a Hash. Like sets, sorted sets are composed of unique, non-repeating string elements, so in some sense a sorted set is a set as well.</p>
</li>
</ul>
<h3>Helpful Links</h3>
<ul>
<li><p><a href="https://redis.io/docs/latest/develop/data-types/sorted-sets">https://redis.io/docs/latest/develop/data-types/sorted-sets</a></p>
</li>
<li><p><a href="https://redis.io/docs/latest/commands/?group=sorted-set">https://redis.io/docs/latest/commands/?group=sorted-set</a></p>
</li>
</ul>
<h2><strong>Streams</strong></h2>
<p>Streams are an append-only log-like data structure.</p>
<h3>Example</h3>
<pre><code class="language-shell">&gt; XADD mystream * sensor-id 1 temperature 22.5
&gt; XREAD COUNT 1 STREAMS mystream 0
1) "mystream"
2) 1) 1) "timestamp"
      2) "sensor-id"
      3) "1"
      4) "temperature"
      5) "22.5"
</code></pre>
<h3>Usage</h3>
<ul>
<li><p>Event sourcing (e.g., tracking user actions, clicks, etc.)</p>
</li>
<li><p>Sensor monitoring (e.g., readings from devices in the field)</p>
</li>
<li><p>Notifications (e.g., storing a record of each user's notifications in a separate stream)</p>
</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li><p>Adding an entry to a stream is O(1)</p>
</li>
<li><p>Accessing any single entry is O(n), where n is the length of the ID.</p>
</li>
<li><p>Consumer groups allow fan-out processing and acknowledgments</p>
</li>
<li><p>Fan-out with Consumer Groups: Multiple consumers in a group distribute messages (load balancing), while independent consumers (outside a group) receive all messages.</p>
</li>
</ul>
<h3>Helpful Links</h3>
<ul>
<li><p><a href="https://redis.io/docs/latest/develop/data-types/streams">https://redis.io/docs/latest/develop/data-types/streams</a></p>
</li>
<li><p><a href="https://redis.io/docs/latest/commands/?group=stream">https://redis.io/docs/latest/commands/?group=stream</a></p>
</li>
</ul>
<h2><strong>Geospatial indexes</strong></h2>
<p>Store and query geographic locations.</p>
<h3>Example</h3>
<pre><code class="language-bash">&gt; GEOADD locations 2.3522 48.8566 "Paris"
&gt; GEOADD locations -74.0060 40.7128 "New York"
&gt; GEODIST locations Paris "New York" km
"5837.2690"
</code></pre>
<h3>Usage</h3>
<ul>
<li><p>Location-based services</p>
</li>
<li><p>Nearby search</p>
</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li>O(log n) complexity for GEOADD, GEODIST, GEORADIUS</li>
</ul>
<h3>Helpful Links</h3>
<ul>
<li><p><a href="https://redis.io/docs/latest/develop/data-types/geospatial">https://redis.io/docs/latest/develop/data-types/geospatial</a></p>
</li>
<li><p><a href="https://redis.io/docs/latest/commands/?group=geo">https://redis.io/docs/latest/commands/?group=geo</a></p>
</li>
</ul>
<h2><strong>Bitmaps</strong></h2>
<p>Bit-level operations for efficient storage and tracking.</p>
<h3>Example</h3>
<pre><code class="language-shell">&gt; SETBIT visits:20240403 0 1
&gt; SETBIT visits:20240403 1 1
&gt; GETBIT visits:20240403 0
1
</code></pre>
<h3>Usage</h3>
<ul>
<li><p>Efficient set representations for cases where the members of a set correspond to the integers 0-N.</p>
</li>
<li><p>Object permissions, where each bit represents a particular permission, similar to the way that file systems store permissions.</p>
</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li><p>SETBIT and GETBIT are O(1)</p>
</li>
<li><p>BITOP is O(n)</p>
</li>
</ul>
<h3>Helpful Links</h3>
<ul>
<li><p><a href="https://redis.io/docs/latest/develop/data-types/bitmaps">https://redis.io/docs/latest/develop/data-types/bitmaps</a></p>
</li>
<li><p><a href="https://redis.io/docs/latest/commands/?group=bitmap">https://redis.io/docs/latest/commands/?group=bitmap</a></p>
</li>
</ul>
<h2><strong>JSON</strong></h2>
<p>Redis JSON provides structured, hierarchical arrays and key-value objects that match the popular JSON text file format.</p>
<h3>Example</h3>
<pre><code class="language-shell">&gt; JSON.SET bike:1 $ '{"brand":"Deimos","model":"X1","colors":["red","black"]}'
OK
&gt; JSON.GET bike:1
"{\"brand\":\"Deimos\",\"model\":\"X1\",\"colors\":[\"red\",\"black\"]}"
&gt; JSON.GET bike:1 $.brand
"[\"Deimos\"]"
</code></pre>
<h3>Usage</h3>
<ul>
<li><p>Storing complex structured data (objects, arrays)</p>
</li>
<li><p>Querying specific fields or nested values with JSONPath ($.field)</p>
</li>
<li><p>Caching structured API responses</p>
</li>
<li><p>Managing hierarchical data like configurations, product catalogs, or user profiles</p>
</li>
</ul>
<h3>Important Notes</h3>
<ul>
<li><p>JSON operations are generally efficient, but performance depends on document size and depth</p>
</li>
<li><p>JSON supports numbers, strings, booleans, nulls, arrays, and objects (full JSON spec)</p>
</li>
<li><p>Memory usage can grow quickly if storing large documents (Danger)</p>
</li>
</ul>
<h3>Helpful Links</h3>
<ul>
<li><a href="https://redis.io/docs/latest/develop/data-types/json">https://redis.io/docs/latest/develop/data-types/json</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Deployment Modes of Redis]]></title><description><![CDATA[Redis can be operated in various setups depending on your application's scalability, availability, and fault-tolerance needs. Below is a summary of each mode:
Standalone
The simplest and default mode.]]></description><link>https://blog.abolfazlmohajeri.ir/deployment-modes-of-redis</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/deployment-modes-of-redis</guid><category><![CDATA[Redis]]></category><category><![CDATA[redis deployment modes]]></category><category><![CDATA[redis standalone]]></category><category><![CDATA[redis master slave]]></category><category><![CDATA[redis cluster]]></category><category><![CDATA[redis sentinel]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 11 Dec 2025 08:24:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/04d0f73e-04ad-4cb5-a4ad-6634453ac434.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Redis can be operated in various setups depending on your application's scalability, availability, and fault-tolerance needs. Below is a summary of each mode:</p>
<h2><strong>Standalone</strong></h2>
<p>The simplest and default mode.</p>
<p>A single Redis instance handles all reads and writes.</p>
<p>Best for development, testing, or low-traffic production environments.</p>
<table style="min-width:50px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><strong>Pros</strong></p></td><td><p><strong>Cons</strong></p></td></tr><tr><td><p>Easy to set up and maintain</p></td><td><p>Single point of failure</p></td></tr><tr><td><p>High performance, single-node does not need to synchronize data, and data has natural consistency</p></td><td><p>No high availability or fault tolerance (When you don't need Redis to keep running in case of a crash)</p></td></tr></tbody></table>

<h2><strong>Master-Slave (Replication)</strong></h2>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765441324973/f0e74701-32d3-4c28-8d25-2f46c8a718b2.png" alt="" style="display:block;margin:0 auto" />

<p>Master-Slave mode consists of N Redis instances, which can be one master with N slaves (if N masters with N slaves, it is not strictly a master-slave mode, and we will discuss it in the cluster mode. N+N Redis instances are required for N masters with N slaves). One function of the master-slave mode is to back up data so that data can be easily recovered when a node is damaged (meaning irreparable hardware damage) because there is a backup. Another function is load balancing. If all clients access one node, it will affect the efficiency of Redis's work. With master-slave, query operations can be completed by querying the slave node. Since master-slave replication means that the data of the master and slave are the same, there is a problem of data redundancy.</p>
<table style="min-width:50px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><strong>Pros</strong></p></td><td><p><strong>Cons</strong></p></td></tr><tr><td><p>Expand the read ability of the master node and share the read pressure of the master node</p></td><td><p>A single machine limits the write ability and storage capacity of the master node</p></td></tr><tr><td><p>The cornerstone of high availability</p></td><td><p>Once the master node fails, the slave node needs to be promoted as the new master node, and the application's master node address needs to be modified. It is also necessary to command all slave nodes to replicate the new master node, which requires manual intervention throughout the process</p></td></tr></tbody></table>

<h2><strong>Sentinel</strong></h2>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765441333539/df2de850-c3dd-4366-8586-d8d45cb09918.png" alt="" style="display:block;margin:0 auto" />

<p>In a master-slave mode, when the master node goes down, the slave node can take over as the master node and continue providing service. However, there is an issue when the IP address of the master node changes. The application service still uses the original master node address to access, requiring manual intervention to modify. Sentinel can precisely solve this problem. Access to data in the Redis cluster is through the Sentinel cluster, which monitors the entire Redis cluster. Once a problem is detected in the Redis cluster, such as the master node going down, the slave node takes over. But when the master node address changes, the application service is unaware and does not need to change the access address because Sentinel interacts with the application service. Sentinel solves the failover problem well, taking high availability to another level. Of course, Sentinel has other functions, such as master node live detection, master-slave operation detection, and master-slave switching. A practical minimum configuration for Redis Sentinel is one master and one slave, monitored by at least three Sentinel nodes to ensure quorum and avoid split-brain.</p>
<table style="min-width:50px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><strong>Pros</strong></p></td><td><p><strong>Cons</strong></p></td></tr><tr><td><p>All the advantages of the master-slave mode</p></td><td><p>Slightly more complex setup</p></td></tr><tr><td><p>High availability</p></td><td><p>Requires configuration of Sentinel nodes</p></td></tr></tbody></table>

<h2><strong>Cluster</strong></h2>
<p>While master-slave replication provides data redundancy and Sentinel ensures automatic failover, these setups still have limitations. A single Redis node’s storage and access capacity is restricted, and scaling write operations across multiple nodes is not possible. Redis Cluster addresses these challenges by offering high availability, horizontal scalability, distribution, and fault tolerance.</p>
<p>For production-ready deployments, a Redis Cluster requires at least three master nodes, each of which can optionally have one or more replica nodes. This ensures data redundancy and allows the cluster to tolerate node failures without downtime. Cluster mode also enables automatic sharding, where data is divided across nodes, allowing the system to handle larger datasets and higher request throughput than single-node setups.</p>
<h3><strong>How does it work?</strong></h3>
<p>Data sharing is achieved through data sharding, while providing data replication and fault transfer functions. In the previous two modes, data was all on one node, and the storage capacity of a single node is limited. Cluster mode shards data and stores it on multiple nodes. When a shard reaches its limit, it is divided into various shards. Data is divided into 16384 slots (hash slots) in the key space of the cluster, and data is distributed to different shards based on hash, as follows:</p>
<p>HASH_SLOT = CRC16(key) % 16384</p>
<p>Data reading and writing after sharding: read requests are assigned to slave nodes, and write requests are assigned to the master node. Data is synchronized from the master to the slave node. Separating read and write requests improves concurrency and increases performance.</p>
<p>Horizontal expansion after data sharding: The master node can be expanded, and data migration is automatically completed within Redis. When you add a new master node, data migration is required, but the Redis service does not need to be offline.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765441200117/260cfcc9-fa29-483a-903d-a8b2cc10b6f1.png" alt="" style="display:block;margin:0 auto" />

<p>For example, there are three master nodes, which means that Redis slots are divided into three segments, assuming the three segments are 0-7000, 7001-12000, and 12001-16383, respectively. Due to business needs, a new master node is added, and the four nodes collectively occupy 16384 slots. The slots need to be re-assigned, and the data must be migrated, but the service does not need to be offline. The Redis-trib management software within Redis performs the Redis cluster's re-sharding. Redis provides all the commands for re-sharding, and Redis-trib performs re-sharding by sending commands to the nodes.</p>
<h3><strong>Why does the Redis cluster have 16384 slots?</strong></h3>
<p>If the slot is 65536, the message header for sending heartbeat messages is too large, reaching 8 KB. As mentioned above, the largest space in the message header is myslots[CLUSTER_SLOTS/8]. When the slot is 65536, the size of this block is 8KB: 65536÷8÷1024=8KB. Since Redis nodes need to send a certain number of ping messages as heartbeat messages every second, if the slot is 65536, the message header of this ping message is too large and wastes bandwidth.</p>
<p>The number of Redis cluster master nodes cannot exceed 1000. As mentioned above, the more cluster nodes there are, the more data is carried in the message body of the heartbeat package. If there are more than 1000 nodes, it will also cause network congestion. Therefore, having more than 1000 Redis cluster nodes is not recommended. For Redis clusters with less than 1000 nodes, 16384 slots are enough. There is no need to expand to 65536. The smaller the slot, the higher the compression ratio with fewer nodes. In the Redis master node's configuration information, the hash slot it is responsible for is saved as a bitmap. During transmission, the bitmap is compressed. However, if the filling rate of the bitmap slots/N is high (N represents the number of nodes), the compression rate of the bitmap is low. If there are few nodes and many hash slots, the compression rate of the bitmap is low.</p>
<h3><strong>Example docker</strong></h3>
<pre><code class="language-dockerfile">name: redis-cluster
services:
  redis-cluster-0:
    container_name: redis-cluster-0
    image: bitnami/redis-cluster:latest
    ports:
      - 6379:6379
    environment:
      - 'ALLOW_EMPTY_PASSWORD=yes'
      - 'REDIS_NODES=redis-cluster-0:6379 redis-cluster-1:6380 redis-cluster-2:6381 redis-cluster-3:6382 redis-cluster-4:6383 redis-cluster-5:6384'
      - 'REDIS_PORT_NUMBER=6379'
      - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=127.0.0.1'
      - 'REDIS_CLUSTER_ANNOUNCE_PORT=6379'
      - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=ip'
    networks:
      - redis-network

  redis-cluster-1:
    container_name: redis-cluster-1
    image: bitnami/redis-cluster:latest
    ports:
      - 6380:6380
    environment:
      - 'ALLOW_EMPTY_PASSWORD=yes'
      - 'REDIS_NODES=redis-cluster-0:6379 redis-cluster-1:6380 redis-cluster-2:6381 redis-cluster-3:6382 redis-cluster-4:6383 redis-cluster-5:6384'
      - 'REDIS_PORT_NUMBER=6380'
      - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=127.0.0.1'
      - 'REDIS_CLUSTER_ANNOUNCE_PORT=6380'
      - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=ip'
    networks:
      - redis-network

  redis-cluster-2:
    container_name: redis-cluster-2
    image: bitnami/redis-cluster:latest
    ports:
      - 6381:6381
    environment:
      - 'ALLOW_EMPTY_PASSWORD=yes'
      - 'REDIS_NODES=redis-cluster-0:6379 redis-cluster-1:6380 redis-cluster-2:6381 redis-cluster-3:6382 redis-cluster-4:6383 redis-cluster-5:6384'
      - 'REDIS_PORT_NUMBER=6381'
      - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=127.0.0.1'
      - 'REDIS_CLUSTER_ANNOUNCE_PORT=6381'
      - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=ip'
    networks:
      - redis-network

  redis-cluster-3:
    container_name: redis-cluster-3
    image: bitnami/redis-cluster:latest
    ports:
      - 6382:6382
    environment:
      - 'ALLOW_EMPTY_PASSWORD=yes'
      - 'REDIS_NODES=redis-cluster-0:6379 redis-cluster-1:6380 redis-cluster-2:6381 redis-cluster-3:6382 redis-cluster-4:6383 redis-cluster-5:6384'
      - 'REDIS_PORT_NUMBER=6382'
      - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=127.0.0.1'
      - 'REDIS_CLUSTER_ANNOUNCE_PORT=6382'
      - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=ip'
    networks:
      - redis-network

  redis-cluster-4:
    container_name: redis-cluster-4
    image: bitnami/redis-cluster:latest
    ports:
      - 6383:6383
    environment:
      - 'ALLOW_EMPTY_PASSWORD=yes'
      - 'REDIS_NODES=redis-cluster-0:6379 redis-cluster-1:6380 redis-cluster-2:6381 redis-cluster-3:6382 redis-cluster-4:6383 redis-cluster-5:6384'
      - 'REDIS_PORT_NUMBER=6383'
      - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=127.0.0.1'
      - 'REDIS_CLUSTER_ANNOUNCE_PORT=6383'
      - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=ip'
    networks:
      - redis-network

  redis-cluster-5:
    container_name: redis-cluster-5
    image: bitnami/redis-cluster:latest
    ports:
      - 6384:6384
    depends_on:
      - redis-cluster-0
      - redis-cluster-1
      - redis-cluster-2
      - redis-cluster-3
      - redis-cluster-4
    environment:
      - 'ALLOW_EMPTY_PASSWORD=yes'
      - 'REDIS_NODES=redis-cluster-0:6379 redis-cluster-1:6380 redis-cluster-2:6381 redis-cluster-3:6382 redis-cluster-4:6383 redis-cluster-5:6384'
      - 'REDIS_PORT_NUMBER=6384'
      - 'REDIS_CLUSTER_ANNOUNCE_HOSTNAME=127.0.0.1'
      - 'REDIS_CLUSTER_ANNOUNCE_PORT=6384'
      - 'REDIS_CLUSTER_PREFERRED_ENDPOINT_TYPE=ip'
      - 'REDIS_CLUSTER_REPLICAS=1'
      - 'REDIS_CLUSTER_CREATOR=yes'
    networks:
      - redis-network

  redis-insight:
    container_name: redis-insight
    image: redis/redisinsight:latest
    ports:
      - 5540:5540
    networks:
      - redis-network

networks:
  redis-network:
    driver: bridge
</code></pre>
<h2><strong>Helpful Definitions</strong></h2>
<table style="min-width:50px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><strong>Term</strong></p></td><td><p><strong>Meaning</strong></p></td></tr><tr><td><p>High Availability</p></td><td><p>The system keeps working even if some parts stop</p></td></tr><tr><td><p>Fault Tolerance</p></td><td><p>The ability of a system to continue working even if some parts fail. It prevents total system failure</p></td></tr><tr><td><p>Failover</p></td><td><p>Switching to a backup system if the main one fails</p></td></tr><tr><td><p>Replication</p></td><td><p>Making copies of data from one server to another</p></td></tr><tr><td><p>Throughput</p></td><td><p>The amount of work or data a system can process in a given amount of time.</p></td></tr><tr><td><p>Latency</p></td><td><p>The time it takes for a request to travel from sender to receiver and get a response.</p></td></tr><tr><td><p>Scalability</p></td><td><p>The system’s ability to handle increased load by adding resources.</p></td></tr></tbody></table>]]></content:encoded></item><item><title><![CDATA[Bypassing Telegram API Restrictions with Cloudflare Workers]]></title><description><![CDATA[The Problem: Telegram API is Blocked in Iran
Telegram’s API is blocked in Iran, making it inaccessible on all local hosting providers. This means that if your bot is hosted on an Iranian server, it ca]]></description><link>https://blog.abolfazlmohajeri.ir/bypassing-telegram-api-restrictions-with-cloudflare-workers</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/bypassing-telegram-api-restrictions-with-cloudflare-workers</guid><category><![CDATA[telegram api]]></category><category><![CDATA[telegram bot]]></category><category><![CDATA[cloudflare-worker]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Wed, 02 Apr 2025 18:10:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/5251e6f5-489a-4a21-9671-3554c27dc18a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>The Problem: Telegram API is Blocked in Iran</h2>
<p>Telegram’s API is blocked in Iran, making it inaccessible on all local hosting providers. This means that if your bot is hosted on an Iranian server, it cannot directly communicate with <code>api.telegram.org</code>.</p>
<p>I recently faced this issue while developing a <strong>Go-based Telegram bot</strong>. At first, in a simple solution, I tried using <strong>proxies</strong> inside my bot’s HTTP client, but I faced many issues and decided to use a better approach.</p>
<h2>The Solution: Cloudflare Workers</h2>
<p>Cloudflare Workers is a serverless platform that allows developers to run lightweight functions at the network edge. I decided to set up a Cloudflare Worker to act as a <strong>reverse proxy</strong>.</p>
<h3>What is a Reverse Proxy?</h3>
<p>A <strong>reverse proxy</strong> is a server that forwards requests on behalf of another server. In this case, instead of your bot directly calling:</p>
<pre><code class="language-plaintext">https://api.telegram.org/bot&lt;TOKEN&gt;/getMe
</code></pre>
<p>It calls:</p>
<pre><code class="language-plaintext">https://YOUR_WORKER_ADDRESS/bot&lt;TOKEN&gt;/getMe
</code></pre>
<p>The Cloudflare Worker then <strong>forwards</strong> the request to Telegram’s API and returns the response back to your bot. This completely <strong>bypasses any regional restrictions</strong>.</p>
<h2>How to Set Up Cloudflare Workers for Telegram API</h2>
<h3>Step 1: Create a Cloudflare Worker</h3>
<ol>
<li><p>Go to Cloudflare Workers and create a new Worker.</p>
</li>
<li><p>Replace the default hello world script with the following code:</p>
</li>
</ol>
<pre><code class="language-javascript">export default {
  async fetch(request) {
      const url = new URL(request.url);
      url.hostname = "api.telegram.org";
      
      const modifiedRequest = new Request(url, {
          method: request.method,
          headers: request.headers,
          body: request.method === "GET" ? null : request.body
      });

      return fetch(modifiedRequest);
  }
};
</code></pre>
<ol>
<li>Save and deploy the Worker.</li>
</ol>
<h3>Step 2: Use the Created Worker</h3>
<p>You just need to replace <code>https://api.telegram.org</code> with <code>https://YOUR_WORKER_ADDRESS</code> in any framework or language. For example, in Go:</p>
<pre><code class="language-go">var (
    Bot              *tgbotapi.BotAPI
    TelegramBotToken string
)

func InitTelegram() {
    TelegramBotToken = os.Getenv("TELEGRAM_BOT_TOKEN")
    proxyURL := os.Getenv("TELEGRAM_PROXY_URL") // https://YOUR_WORKER_ADDRESS
    var err error
    Bot, err = tgbotapi.NewBotAPIWithAPIEndpoint(TelegramBotToken, proxyURL+"/bot%s/%s")
    if err != nil {
        log.Fatal(err)
        return
    }
    log.Printf("Authorized on account %s", Bot.Self.UserName)
}
</code></pre>
<h2>Why Use Cloudflare Workers?</h2>
<ul>
<li><p><strong>No need for a VPS</strong></p>
</li>
<li><p><strong>Fast and free (With some limitation)</strong></p>
</li>
<li><p><strong>Easy to deploy and maintain</strong></p>
</li>
<li><p><strong>Bypasses regional restrictions effortlessly</strong></p>
</li>
</ul>
<h2>Conclusion</h2>
<p>If you are facing <strong>Telegram API restrictions in Iran or any other country</strong>, Cloudflare Workers is a <strong>powerful and free solution</strong> to keep your bot running smoothly. By simply rerouting requests through a Worker, you can avoid direct blocks and enjoy <strong>uninterrupted access</strong> to Telegram’s API.</p>
<p><strong>Have you tried this solution? Let me know your experience in the comments!</strong></p>
]]></content:encoded></item><item><title><![CDATA[Map Tools]]></title><description><![CDATA[In my free time these days, I developed a small but highly practical tool for maps called Map Tools.
What is Map Tools & Who is it for?
Map Tools is an online map assistant that provides utilities suc]]></description><link>https://blog.abolfazlmohajeri.ir/map-tools</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/map-tools</guid><category><![CDATA[map-tools]]></category><category><![CDATA[geometry extraction]]></category><category><![CDATA[coordinate conversion]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[GIS]]></category><category><![CDATA[Spatial data]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Tue, 25 Mar 2025 15:53:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/7dd04d13-7f1b-4230-bdb9-89f5bc9289b6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my free time these days, I developed a small but highly practical tool for maps called Map Tools.</p>
<h2>What is Map Tools &amp; Who is it for?</h2>
<p>Map Tools is an online map assistant that provides utilities such as geometry extraction, coordinate conversion, and WKT (Well-Known Text) visualization. Whether you're a GIS professional, developer, or anyone working with spatial data, this tool makes these tasks easier and more efficient.</p>
<p>I built Map Tools to streamline repetitive tasks I often encountered in GIS and location-based projects.</p>
<h2>Check it out</h2>
<p>GitHub: <a href="http://github.com/abmohajeri/map-tools">github.com/abmohajeri/map-tools</a></p>
<p>Live Demo: <a href="http://abmohajeri.github.io/map-tools">abmohajeri.github.io/map-tools</a></p>
<h2>Contributions &amp; Support</h2>
<p>If you find Map Tools useful, give it a ⭐ on GitHub! Your support helps improve the project.</p>
<p>I’d also love your feedback—try it out, fork the repo, submit issues, or create pull requests.</p>
<p>Let’s build something great together! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[What is Photogrammetry?]]></title><description><![CDATA[Photogrammetry is the science and technology of obtaining reliable information about physical objects and the environment through the process of recording, measuring, and interpreting photographic ima]]></description><link>https://blog.abolfazlmohajeri.ir/what-is-photogrammetry</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/what-is-photogrammetry</guid><category><![CDATA[photogrammetry]]></category><category><![CDATA[3d mesh]]></category><category><![CDATA[meshroom]]></category><category><![CDATA[fotros]]></category><category><![CDATA[3d model]]></category><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 12 Sep 2024 07:28:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/a98352d2-6b68-4922-bf80-1e3740922f47.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Photogrammetry is the science and technology of obtaining reliable information about physical objects and the environment through the process of recording, measuring, and interpreting photographic images. Essentially, it allows for the creation of three-dimensional models or maps from two-dimensional photos. This technique has applications in various fields, from archaeology to urban planning, architecture, and even game development.</p>
<p>With the rise of photogrammetric tools and software, creating highly detailed 3D models has become easier and more accessible. Photogrammetry combines photography, geometry, and advanced algorithms to deliver models that are accurate and visually stunning.</p>
<p>In this article, we’ll dive into the basics of photogrammetry, how it works, and some simple examples that showcase its potential. We’ll also take a look at demo of my own photogrammetry model, <strong>Fotros</strong>, available on Hugging Face <a href="https://huggingface.co/spaces/abmohajeri/fotros">here</a>, which demonstrates the process in action.</p>
<h3>How Photogrammetry Works</h3>
<p>At its core, photogrammetry relies on multiple photos taken from different angles of the same object or scene. By using triangulation, the software can calculate the distance and positions of points on the object from these images. Here’s a simplified breakdown of the process:</p>
<ol>
<li><p><strong>Image Acquisition</strong>: Multiple photos are taken from different viewpoints. Ideally, the object or scene should be captured from various angles to ensure the maximum amount of detail is recorded.</p>
</li>
<li><p><strong>Processing</strong>: The photos are fed into photogrammetry software that analyzes the overlapping areas in the images. Key points (or "features") in the images are identified and matched across multiple photos.</p>
</li>
<li><p><strong>Triangulation</strong>: Using the positions of the cameras and the identified matching features in the photos, the software calculates the relative positions of these points in 3D space, effectively reconstructing the object.</p>
</li>
<li><p><strong>3D Model Creation</strong>: Finally, the software generates a point cloud—a collection of 3D coordinates representing the object. These points can be further refined into a 3D mesh and textured to create a realistic 3D model.</p>
</li>
</ol>
<p>Here is example of multiple photos taken from different angles of the same object:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726125747618/7e677864-8be8-45a6-8bfb-ffdc5c84745a.png" alt="" style="display:block;margin:0 auto" />

<h3>List of the best photogrammetry apps</h3>
<p>Here’s a list of the best photogrammetry apps:</p>
<ol>
<li><p><a href="https://www.capturingreality.com/"><strong>RealityCapture</strong></a><strong>:</strong> A fast and efficient photogrammetry tool, popular for handling large datasets and high-resolution photos. It is used for creating highly accurate 3D models of landscapes and objects.</p>
</li>
<li><p><a href="https://www.agisoft.com/"><strong>Agisoft Metashape</strong></a><strong>:</strong> A professional-grade software known for its high accuracy and flexibility, supporting small and large projects for industries like archaeology, architecture, and aerial mapping.</p>
</li>
<li><p><a href="https://www.3dflow.net/3df-zephyr-photogrammetry-software/"><strong>3DF Zephyr</strong></a><strong>:</strong> A versatile photogrammetry tool that offers both free and paid versions. It is used to create precise 3D reconstructions of objects, buildings, and landscapes.</p>
</li>
<li><p><a href="https://github.com/alicevision/Meshroom"><strong>Meshroom</strong></a><strong>:</strong> An open-source photogrammetry tool that allows users to create detailed 3D models from photos. It is user-friendly and suitable for both beginners and professionals.</p>
</li>
</ol>
<h3>Real-World Application: <a href="https://huggingface.co/spaces/abmohajeri/fotros">Fotros</a></h3>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726126460807/f2a09384-605f-4899-a532-79f37cb661c0.jpeg" alt="" style="display:block;margin:0 auto" />

<p>To showcase the power of photogrammetry, I developed a model called <a href="https://huggingface.co/spaces/abmohajeri/fotros"><strong>Fotros</strong></a>, which is available on Hugging Face. Fotros allows users to upload photos of an object from various angles, and it generates a 3D model using photogrammetry techniques.</p>
<p>This model is a practical example of how easy it can be to turn photos into 3D models. It uses Meshroom as the backend for photogrammetry and is integrated with advanced algorithms to make the process fast and accurate. It’s ideal for beginners and professionals alike who are interested in creating 3D models for augmented reality (AR), virtual reality (VR), and other visualizations.</p>
<h3>Conclusion</h3>
<p>Photogrammetry is an incredible tool that turns ordinary photos into precise 3D models, opening the door for countless applications. Whether you're documenting an archaeological site or creating 3D assets for video games, photogrammetry offers a powerful solution.</p>
<p>With models like <a href="https://huggingface.co/spaces/abmohajeri/fotros"><strong>Fotros</strong></a>, the process is becoming more accessible than ever. If you're interested in seeing photogrammetry in action, I encourage you to try out Fotros and start exploring the world of 3D modeling!</p>
]]></content:encoded></item><item><title><![CDATA[Explainable question answering system]]></title><description><![CDATA[This work represents one of the first efforts to bring explainability into Persian language QA systems, leveraging the powerful BERT model.
Overview of BERT
BERT (Bidirectional Encoder Representations]]></description><link>https://blog.abolfazlmohajeri.ir/explainable-question-answering-system</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/explainable-question-answering-system</guid><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 04 May 2023 08:14:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/d9c444b0-91bd-4348-bb6d-86357e7630d6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This work represents one of the first efforts to bring explainability into Persian language QA systems, leveraging the powerful BERT model.</p>
<h3>Overview of BERT</h3>
<p><strong>BERT (Bidirectional Encoder Representations from Transformers)</strong> is a groundbreaking model developed by Google for natural language understanding tasks. It is designed to understand the context of words in a sentence by looking at the words before and after them. BERT has revolutionized the field of NLP because it is pre-trained on a vast amount of text and can be fine-tuned for specific tasks like question answering, sentiment analysis, and more.</p>
<h3>Overview of Question Answering Systems</h3>
<p><strong>Question Answering (QA) systems</strong> are a type of AI that can automatically answer questions posed by humans in natural language. These systems typically involve two main components:</p>
<ol>
<li><p><strong>Contextual Understanding</strong>: The model comprehends the context of the input text (e.g., a paragraph).</p>
</li>
<li><p><strong>Answer Extraction</strong>: The model identifies and extracts the relevant answer from the context based on the given question.</p>
</li>
</ol>
<p>In the context of Persian language processing, building such systems poses unique challenges due to the complexity of the language, as well as the limited availability of high-quality annotated data.</p>
<h3>Explainable Persian QA with BERT</h3>
<p>In this project, i implemented a Persian QA system using BERT, specifically tailored for the Persian language. What sets this work apart is the focus on <strong>explainability</strong>—ensuring that the model’s decisions and answer predictions are transparent and understandable to users. This is particularly important in sensitive applications where understanding the reasoning behind an AI’s response is crucial.</p>
<h3><strong>Access the Code</strong></h3>
<p>The code for this model, along with detailed documentation, is available on GitHub. he repository includes all the necessary resources for implementing the explainable Persian QA system.</p>
<p><a href="https://github.com/abmohajeri/explainable-question-answering/blob/main/xai-qa.ipynb"><strong>GitHub Repository</strong></a></p>
<h3><strong>Presentation at ICCKE 2022</strong></h3>
<p>This project was presented at the International Conference on Computer and Knowledge Engineering (ICCKE) in 2022. The conference provided an excellent platform to share and discuss cutting-edge research with peers from around the world.</p>
<p>If you are interested in a more in-depth explanation, including the technical aspects of the model, you can watch the video of my presentation, conducted in Persian:</p>
<p><a class="embed-card" href="https://www.youtube.com/embed/9mQxD6wWD80">https://www.youtube.com/embed/9mQxD6wWD80</a></p>
]]></content:encoded></item><item><title><![CDATA[Explainable CNN model on fashion mnist dataset]]></title><description><![CDATA[This blog post introduces an explainable Convolutional Neural Network (CNN) model I developed for the Fashion MNIST dataset. The work was initially presented at the ICCKE 2022 conference.
Overview of ]]></description><link>https://blog.abolfazlmohajeri.ir/explainable-cnn-model-on-fashion-mnist-dataset</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/explainable-cnn-model-on-fashion-mnist-dataset</guid><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 04 May 2023 08:11:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/e61d3237-b3d1-45cf-b62a-721c537f1502.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This blog post introduces an explainable Convolutional Neural Network (CNN) model I developed for the Fashion MNIST dataset. The work was initially presented at the ICCKE 2022 conference.</p>
<h3>Overview of the CNN Model</h3>
<p>CNNs have become the go-to models for image classification tasks due to their ability to automatically and adaptively learn spatial hierarchies of features through backpropagation. The model I developed is designed not only for accuracy but also for interpretability—providing insights into <em>why</em> the model makes certain predictions.</p>
<p>I used Fashion MNIST dataset. The Fashion MNIST dataset is a popular alternative to the traditional MNIST dataset, featuring 70,000 grayscale images of 10 different categories of clothing items, such as shirts, sneakers, and dresses. The challenge lies in correctly classifying these items into their respective categories.</p>
<h3>Explainability</h3>
<p>Explainability in AI is about making the inner workings of machine learning models transparent. This is especially important in fields where trust and accountability are paramount. By incorporating explainability techniques, the model provides a clearer understanding of how it reaches its conclusions.</p>
<p>I explain the concept of Explainable AI <a href="https://blog.abolfazlmohajeri.ir/what-is-explainable-artificial-intelligence">in this article</a>.</p>
<h3>Access the Code</h3>
<p>The code for this model, along with detailed documentation, is available on GitHub. It includes not only the implementation of the CNN but also the methods for visualizing and explaining the results.</p>
<p><a href="https://github.com/abmohajeri/explainable-cnn/blob/main/xai-cnn.ipynb">GitHub Repository</a></p>
<h3>Presentation at ICCKE 2022</h3>
<p>This project was presented at the International Conference on Computer and Knowledge Engineering (ICCKE) in 2022. The conference provided an excellent platform to share and discuss cutting-edge research with peers from around the world.</p>
<p>If you are interested in a more in-depth explanation, including the technical aspects of the model, you can watch the video of my presentation, conducted in Persian:</p>
<p><a class="embed-card" href="https://www.youtube.com/embed/G01Xtzp_tzM">https://www.youtube.com/embed/G01Xtzp_tzM</a></p>
]]></content:encoded></item><item><title><![CDATA[What is explainable artificial intelligence?]]></title><description><![CDATA[Today, with the advancement of artificial intelligence models and the development of models such as deep neural networks, understanding the cause of the results of these models has become impossible. ]]></description><link>https://blog.abolfazlmohajeri.ir/what-is-explainable-artificial-intelligence</link><guid isPermaLink="true">https://blog.abolfazlmohajeri.ir/what-is-explainable-artificial-intelligence</guid><dc:creator><![CDATA[Abolfazl Mohajeri]]></dc:creator><pubDate>Thu, 04 May 2023 07:09:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/64529bee5b3d88bba8b0e0e5/2f419f48-38ec-41d9-aafa-4123079abfb1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today, with the advancement of artificial intelligence models and the development of models such as deep neural networks, understanding the cause of the results of these models has become impossible. These models, which are generally called black boxes, are used in many artificial intelligence activities because of their very good results. Although these models perform very well in many AI activities, it is not clear how they work to produce a specific prediction. Now we need to find a way to make these models transparent, which helps to understand how they make decisions.</p>
<h3>Explainable Artificial Intelligence</h3>
<p>In the past few years, a hot and important concept called artificial intelligence has been expressed, which generally refers to the methods and techniques that can be used to explain how artificial intelligence works and how to clarify the decision-making of black box models for humans. In general, these explanations provide an answer to the question of why a model predicts an outcome. For example, consider the following model:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1683185602229/bf746e82-e185-4b73-8417-ee048cf3266b.png" alt="" style="display:block;margin:0 auto" />

<p>As shown in the figure above, the process of many machine learning models is that a black box is first trained by the dataset and that black box is used to make predictions on new inputs. It is clear that the function of the black box is not obvious, therefore, explainable artificial intelligence adds clarity to such models by providing a variety of explanations and turning them into a transparent model like the one below:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1683185602229/bf746e82-e185-4b73-8417-ee048cf3266b.png" alt="" style="display:block;margin:0 auto" />

<h3>Explanations and their types</h3>
<p>As mentioned earlier, providing explanations is a way to add transparency to various AI black box models. Computer scientists have tried for decades to open these black boxes and increase the transparency of such models by providing explanations. A recent research review shows that these efforts have yielded good results. Therefore, explanations can be considered as information about the results and model predictions, which helps to better understand the performance of the model. These explanations can be presented in the following types:</p>
<ul>
<li><p><strong>Textual explanations:</strong> In this type of explanation, a textual explanation is provided that shows the function of the model. Texts in this type of explanation can be simply presented or generated by the learning process. For example, the output of \(x_1\) is equal to \(y_1\) because \(x^3&gt;20\).</p>
</li>
<li><p><strong>Model simplification:</strong> In this type of explanation, a completely new model is reconstructed based on the previously trained model. This new and simplified model tries to optimize the previous complex model and reduce its complexity by maintaining its performance. For example, part a of the figure below shows this type of explanation.</p>
</li>
<li><p><strong>Visualization:</strong> Visualization means being able to display and visualize the behavior of the model and its complex interactions through a visual display, for example, in the form of charts. For example, part b of the figure below shows an example of this explanation.</p>
</li>
<li><p><strong>Local explanations:</strong> This explains the part performance of the entire system by dividing the model space into smaller subspaces and examining them.</p>
</li>
<li><p><strong>Feature importance:</strong> This explanation calculates the importance of a feature on the model prediction, comparing and checking the importance of this feature in different models can give very good information about the performance of those models. Part c of the figure below refers to this type of explanation.</p>
</li>
<li><p><strong>Explanation by example:</strong> This means that the sample extracted data from the model by which we can have a better understanding of the model performance. For example, input 3 leads to output 10.</p>
</li>
</ul>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1683185478610/cefbd2c7-f825-42a8-951f-98a0936f6fb3.png" alt="" style="display:block;margin:0 auto" />

<h3>Conclusion</h3>
<p>By utilizing these explanation techniques, Explainable AI (XAI) not only enhances the transparency of AI models but also builds trust in AI-driven systems, making them more reliable for critical decision-making processes. As AI continues to integrate into various aspects of our lives, the importance of explainability cannot be overstated. It ensures that AI is not just powerful but also understandable and accountable.</p>
<p>For practical examples of XAI in action, you might be interested in exploring the following articles:</p>
<ul>
<li><p><a href="https://blog.abolfazlmohajeri.ir/explainable-cnn-model-on-fashion-mnist-dataset">Explainable CNN Model on the Fashion MNIST Dataset</a></p>
</li>
<li><p><a href="https://blog.abolfazlmohajeri.ir/explainable-question-answering-system">Explainable Question Answering System</a></p>
</li>
</ul>
<p>These articles delve into specific applications of XAI, offering insights into how explainability is applied in real-world scenarios.</p>
]]></content:encoded></item></channel></rss>