<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aniket Bhattacharyea</title>
    <description>The latest articles on DEV Community by Aniket Bhattacharyea (@heraldofsolace).</description>
    <link>https://dev.to/heraldofsolace</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F27791%2F94532916-fa4c-4972-a381-f219844eff22.png</url>
      <title>DEV Community: Aniket Bhattacharyea</title>
      <link>https://dev.to/heraldofsolace</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/heraldofsolace"/>
    <language>en</language>
    <item>
      <title>The Best AI Code Review Tools of 2026</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Fri, 20 Feb 2026 13:25:32 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/the-best-ai-code-review-tools-of-2026-2mb3</link>
      <guid>https://dev.to/heraldofsolace/the-best-ai-code-review-tools-of-2026-2mb3</guid>
      <description>&lt;p&gt;Early AI code review tools had a math problem. For every real bug they caught, they flagged nine false positives. Teams got buried in comments about variable naming conventions and whitespace. They started ignoring the bot entirely. Productivity tanked.&lt;/p&gt;

&lt;p&gt;The tools that made it past 2025 didn't just throw better models at the problem. They rebuilt how code review works from scratch.&lt;/p&gt;

&lt;p&gt;This guide covers the AI code review tools worth using in 2026, what they're actually good at, and which specific problems they solve for your team.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Problem: Context Windows vs. Large Diffs
&lt;/h2&gt;

&lt;p&gt;AI reviewers break down when you feed them too much code at once. A 1,000-line diff overwhelms the context window. The model loses coherence, misses connections between changes, and falls back on pattern matching for style issues.&lt;/p&gt;

&lt;p&gt;The same reviewer that produces noise on large diffs produces useful feedback on small ones. The tool didn't get smarter. You gave it a problem it could actually solve.&lt;/p&gt;

&lt;p&gt;This is why the effective tools in 2026 either enforce small changes (Graphite), sacrifice depth for speed (GitHub Copilot), or index your entire codebase upfront (Greptile).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13qiqz7gotd26i1961wa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13qiqz7gotd26i1961wa.png" alt="AI code review tools" width="800" height="977"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool Comparison Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Platform Support&lt;/th&gt;
&lt;th&gt;Analysis Depth&lt;/th&gt;
&lt;th&gt;False Positive Rate&lt;/th&gt;
&lt;th&gt;Price/User/Mo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Graphite Agent&lt;/td&gt;
&lt;td&gt;Teams adopting stacked PRs&lt;/td&gt;
&lt;td&gt;GitHub only&lt;/td&gt;
&lt;td&gt;Deep (full codebase)&lt;/td&gt;
&lt;td&gt;~3% unhelpful&lt;/td&gt;
&lt;td&gt;\$40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Copilot&lt;/td&gt;
&lt;td&gt;Existing Copilot users&lt;/td&gt;
&lt;td&gt;GitHub only&lt;/td&gt;
&lt;td&gt;Surface (diff-based)&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;\$10-39 (bundled)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeRabbit&lt;/td&gt;
&lt;td&gt;Multi-platform teams&lt;/td&gt;
&lt;td&gt;GitHub, GitLab, Bitbucket, Azure&lt;/td&gt;
&lt;td&gt;Surface (diff-based)&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;\$24-30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Greptile&lt;/td&gt;
&lt;td&gt;Maximum bug detection&lt;/td&gt;
&lt;td&gt;GitHub, GitLab&lt;/td&gt;
&lt;td&gt;Deep (full codebase)&lt;/td&gt;
&lt;td&gt;Highest&lt;/td&gt;
&lt;td&gt;\$30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BugBot&lt;/td&gt;
&lt;td&gt;Cursor-native teams&lt;/td&gt;
&lt;td&gt;GitHub only&lt;/td&gt;
&lt;td&gt;Medium (8-pass diff)&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;td&gt;\$40 + Cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Graphite Agent
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://graphite.com/features/agent" rel="noopener noreferrer"&gt;Graphite Agent&lt;/a&gt; combines full-codebase understanding with &lt;a href="https://graphite.dev/guides/stacked-diffs" rel="noopener noreferrer"&gt;stacked PRs&lt;/a&gt;. Instead of one massive pull request, you break changes into small, dependent PRs that merge in sequence.&lt;/p&gt;

&lt;p&gt;Here's how stacked PRs work:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc4trtetydfbmpxtriqig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc4trtetydfbmpxtriqig.png" alt="Stacked PR" width="800" height="124"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shopify reported 33% more PRs merged per developer after adoption, with 75% of PRs now going through Graphite. &lt;a href="https://graphite.com/customer/asana" rel="noopener noreferrer"&gt;Asana&lt;/a&gt; saw engineers save 7 hours weekly, ship 21% more code, and cut median PR size by 11%.&lt;/p&gt;

&lt;p&gt;Agent maintains an &lt;a href="https://graphite.com/guides/effectiveness-and-limitations-of-ai-code-review" rel="noopener noreferrer"&gt;unhelpful comment rate under 3%&lt;/a&gt;. When it flags an issue, developers change the code 55% of the time. Human reviewers hit 49%.&lt;/p&gt;

&lt;p&gt;The tool provides one-click fixes, resolves CI failures inline, and includes a &lt;a href="https://graphite.dev/features/merge-queue" rel="noopener noreferrer"&gt;merge queue&lt;/a&gt; that coordinates landing changes in order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The constraint:&lt;/strong&gt; GitHub-only, and your entire team needs to adopt stacked workflows. For teams that commit to this change, median PR merge time drops from 24 hours to 90 minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; &lt;a href="https://graphite.dev/pricing" rel="noopener noreferrer"&gt;Team plan&lt;/a&gt; at \$40/user/month with unlimited reviews. Free tier for individuals. Enterprise pricing on request.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub Copilot Code Review
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.github.com/en/copilot/using-github-copilot/code-review/using-copilot-code-review" rel="noopener noreferrer"&gt;GitHub Copilot Code Review&lt;/a&gt; hit general availability in April 2025 and reached 1 million users in a month. You assign Copilot as a reviewer like any teammate. It leaves inline comments with suggested fixes.&lt;/p&gt;

&lt;p&gt;The October 2025 update added context gathering. Copilot now reads source files, explores directory structure, and integrates &lt;a href="https://codeql.github.com/" rel="noopener noreferrer"&gt;CodeQL&lt;/a&gt; and ESLint for security scanning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it's good at:&lt;/strong&gt; Zero friction if you already pay for Copilot. Catches typos, null checks, and simple logic errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it misses:&lt;/strong&gt; Architectural problems and cross-file dependencies. It's diff-based, so it only sees what changed in the PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Bundled with Copilot subscriptions (\$10-39/month depending on tier). Code review features not available on free tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  CodeRabbit
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://coderabbit.ai/" rel="noopener noreferrer"&gt;CodeRabbit&lt;/a&gt; is the most widely installed AI code review app on GitHub and GitLab. Over 2 million repositories connected, 13 million+ PRs processed. It runs automatically on new PRs, leaving line-by-line comments with severity rankings and one-click fixes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The advantage:&lt;/strong&gt; Platform breadth. Supports GitHub, GitLab, Bitbucket, and Azure DevOps. Integrates 40+ linters and SAST scanners. Offers self-hosted deployment for enterprises with 500+ seats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The limitation:&lt;/strong&gt; Diff-based analysis. It sees what changed in the PR, not how changes interact with your codebase. Independent benchmarks gave it a 1/5 completeness score for catching systemic issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; Pro plan at \$24-30/user/month. Free tier with basic PR summaries. Enterprise plans with self-hosting available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Greptile
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.greptile.com/" rel="noopener noreferrer"&gt;Greptile&lt;/a&gt; indexes your entire repository and builds a code graph. It uses multi-hop investigation to trace dependencies, check git history, and follow leads across files.&lt;/p&gt;

&lt;p&gt;Version 3 (late 2025) uses the Anthropic Claude Agent SDK for autonomous investigation. The tool shows you evidence from your codebase for every flagged issue.&lt;/p&gt;

&lt;p&gt;At \$30/developer/month with a \$180M valuation after its Benchmark-led Series A, Greptile offers the deepest context-aware analysis available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tradeoff:&lt;/strong&gt; Highest catch rate, but also highest false positive rate in independent evaluations. You get more real bugs and more noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; \$30/developer/month for unlimited reviews. Discounts for annual commitments. Open-source projects may qualify for free usage. Self-hosted and enterprise pricing on request.&lt;/p&gt;

&lt;h2&gt;
  
  
  BugBot
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cursor.com/bugbot" rel="noopener noreferrer"&gt;BugBot&lt;/a&gt; from Cursor launched in July 2025 and reviews 2 million+ PRs monthly. It runs 8 parallel review passes with randomized diff order on every PR, catching bugs that single-pass reviewers miss.&lt;/p&gt;

&lt;p&gt;The "Fix in Cursor" button jumps you from review comment to editor with the fix pre-loaded. Discord's engineering team reported BugBot finding real bugs on human-approved PRs. Over 70% of flagged issues get resolved before merge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The constraint:&lt;/strong&gt; Tightly coupled to Cursor. You need a Cursor subscription, and it works best when your team already uses Cursor as their primary editor.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt; \$40/user/month plus Cursor subscription. 14-day free trial. GitHub-only.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Smaller PRs Get Better Results
&lt;/h2&gt;

&lt;p&gt;Research shows 30-40% cycle time improvements for PRs under 500 lines, with diminishing returns above that threshold. Teams using stacked PRs ship 20% more code with 8% smaller median PR size, saving roughly 10 hours per week waiting to merge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzh98xu4zf6n023ldayh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhzh98xu4zf6n023ldayh.png" alt="Bugbot" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same AI reviewer produces signal on a 150-line diff and noise on a 1,000-line one. The tool didn't change. The workflow gave it a solvable problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision Framework
&lt;/h2&gt;

&lt;p&gt;Pick based on what you're willing to change:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No workflow changes:&lt;/strong&gt; Start with GitHub Copilot if you already pay for it. Zero setup, catches obvious bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-platform support:&lt;/strong&gt; CodeRabbit is the only option that works across GitHub, GitLab, Bitbucket, and Azure DevOps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maximum bug detection:&lt;/strong&gt; Greptile's full-codebase indexing finds issues other tools miss. Accept higher noise as the tradeoff.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor workflow:&lt;/strong&gt; If your team lives in Cursor, BugBot extends your existing setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow transformation:&lt;/strong&gt; Graphite treats &lt;a href="https://graphite.dev/guides/code-review-best-practices" rel="noopener noreferrer"&gt;code review&lt;/a&gt; as a systems problem. The numbers from Shopify (33% more PRs per developer) and Asana (7 hours saved weekly) came from adopting stacked workflows, not just adding AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration and Security
&lt;/h2&gt;

&lt;p&gt;AI code review tools plug directly into GitHub, GitLab, and Bitbucket as automated reviewers. They integrate with CI/CD pipelines, offer IDE plugins for real-time feedback, and support webhook triggers for automatic reviews on code push.&lt;/p&gt;

&lt;p&gt;Security varies by provider. Look for encryption in transit and at rest, SOC 2 compliance, and clear data retention policies. Some tools offer self-hosted options for maximum control. Graphite has a &lt;a href="https://graphite.com/docs/privacy-and-security" rel="noopener noreferrer"&gt;privacy-first approach&lt;/a&gt; that guarantees code stays private and isn't used for model training.&lt;/p&gt;

&lt;p&gt;Cost ranges from \$10-50 per user monthly for standard plans. GitHub Copilot Code Review bundles with existing subscriptions (\$10-39/month). Enterprise plans with custom rules and dedicated support cost more. Self-hosted options may use infrastructure-based pricing instead of per-user costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Matters
&lt;/h2&gt;

&lt;p&gt;The AI code review tools that survived 2025 didn't just add smarter models. They rethought workflows. Graphite built a platform around stacked changes. GitHub Copilot traded depth for zero friction. CodeRabbit went for breadth across platforms. Greptile went all-in on context. BugBot integrated tightly with an editor.&lt;/p&gt;

&lt;p&gt;The right tool depends on what you're willing to change. If you want AI review with no disruption, GitHub Copilot works. If you need multi-platform support, CodeRabbit is your only option. If catching deep bugs matters more than noise, Greptile's full-codebase indexing finds things others miss. If your team lives in Cursor, BugBot fits naturally.&lt;/p&gt;

&lt;p&gt;If you're willing to change how your team works, not just add a bot, Graphite treats code review as a workflow problem. Stacked PRs, AI review, and merge queue work together in ways that separate tools can't replicate. The productivity gains came from the workflow change, not just the AI.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>code</category>
      <category>github</category>
    </item>
    <item>
      <title>Stacking up Graphite in the World of Code Review Tools</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Fri, 23 Jan 2026 13:07:13 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/stacking-up-graphite-in-the-world-of-code-review-tools-5fbn</link>
      <guid>https://dev.to/heraldofsolace/stacking-up-graphite-in-the-world-of-code-review-tools-5fbn</guid>
      <description>&lt;p&gt;&lt;strong&gt;A developer's guide to navigating the crowded market of AI code review assistants in 2026.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The "Review Bot" Explosion
&lt;/h2&gt;

&lt;p&gt;2025 is the year every engineering team tried to automate code review. If you opened a pull request this year, chances are an AI left a comment on it. Some of those comments caught real bugs. Many did not.&lt;/p&gt;

&lt;p&gt;The problem isn't that AI code review doesn't work. It's that the market is flooded with tools that look similar on the surface but solve fundamentally different problems. One tool might catch off-by-one errors in your IDE before you even push. Another scans your entire repository history to understand cross-file dependencies. A third lives in your GitHub comments, leaving feedback that ranges from brilliant to pedantic.&lt;/p&gt;

&lt;p&gt;For the average developer or tech lead, it's hard to tell the difference between a wrapper around an LLM and an actual platform. This article cuts through that noise.&lt;/p&gt;

&lt;p&gt;I'll compare five tools—Graphite, GitHub's native review, CodeRabbit, Greptile, and BugBot—using three criteria:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Depth&lt;/strong&gt;: Does it understand your whole codebase, or just the diff?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflow&lt;/strong&gt;: Does it change how you review, or just what you read?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Noise&lt;/strong&gt;: Does it help you ship faster, or spam your PR timeline?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's start by categorizing these tools by what they actually do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 1: The "Workflow Platform"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwg0xz22xzi72a6d6yf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwg0xz22xzi72a6d6yf1.png" alt="Graphite interface" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool: Graphite Agent
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://graphite.com" rel="noopener noreferrer"&gt;Graphite&lt;/a&gt; is an AI-augmented code review and pull request workflow platform designed to help teams ship higher-quality code faster by rethinking how reviews and merges work. It integrates AI feedback directly into pull requests, supports stacked pull request workflows, and provides automated merge orchestration.&lt;/p&gt;

&lt;p&gt;When you open a pull request with Graphite, the Graphite Agent provides AI-driven feedback directly in the PR page. It focuses on delivering high-signal insights rather than large volumes of superficial comments. The platform is built to help teams catch substantive bugs and issues early in the review cycle. &lt;/p&gt;

&lt;p&gt;A key workflow innovation is &lt;strong&gt;stacked pull requests&lt;/strong&gt;. With the Graphite CLI and tooling, developers can create a series of dependent PRs—each building on the last—so you can continue working on PR #3 while PR #1 and #2 are still under review. When an earlier PR merges, subsequent stacked changes are automatically rebased, helping reduce merge conflicts and keeping development unblocked.&lt;/p&gt;

&lt;p&gt;Graphite also includes a &lt;strong&gt;stack-aware merge queue&lt;/strong&gt;. Instead of merging PRs sequentially and running CI for each one in isolation, the merge queue can batch and test multiple PRs in parallel once they’re ready, speeding up the merge process and reducing wait times. &lt;/p&gt;

&lt;p&gt;In addition to AI review and stacking workflows, Graphite offers a modern PR review interface with unified inboxes, GitHub integrations, a CLI, and editor extensions (e.g., VS Code). These features aim to streamline the development and review experience and reduce context switching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: Teams that find traditional GitHub review workflows slow or fragmented, and that would benefit from structured, smaller changesets, automated rebasing, and integrated AI feedback throughout the pull request lifecycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free (Hobby)&lt;/strong&gt;: Essentials, including CLI for stacked PRs, VS Code extension, and limited access to Graphite Agent and AI reviews.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starter&lt;/strong&gt;: $20/user/month (billed annually), includes support for all GitHub organization repos and team insights.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team&lt;/strong&gt;: $40/user/month (billed annually), adds unlimited Graphite Agent access, unlimited AI reviews, review customizations, automations, and merge queue.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise&lt;/strong&gt;: Custom pricing with advanced controls, security features, and support. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Category 2: The "Native Giant"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqougjqe12r58pvt3kgd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqougjqe12r58pvt3kgd.png" alt="Enabling Copilot in VS Code" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool: GitHub
&lt;/h2&gt;

&lt;p&gt;GitHub’s native code review workflow is the baseline most teams start from. It is reliable, familiar, and requires no additional tooling beyond what teams are already using for source control and pull requests.&lt;/p&gt;

&lt;p&gt;GitHub has added AI-assisted review features through Copilot (including Copilot for Business and Copilot Enterprise). These capabilities can summarize pull requests, explain changes, and suggest improvements directly in comments. In practice, the quality of these suggestions varies: some feedback is genuinely helpful, while other comments are generic or low value. Importantly, Copilot acts as an assistive layer rather than a fully autonomous review agent.&lt;/p&gt;

&lt;p&gt;A core limitation is architectural. GitHub’s pull request model is fundamentally linear: one branch, one PR, one review lifecycle. There is no native support for stacked or dependent pull requests, which makes it harder to work on large changes incrementally without introducing workflow friction.&lt;/p&gt;

&lt;p&gt;For static analysis and security scanning, GitHub offers &lt;a href="https://codeql.github.com/" rel="noopener noreferrer"&gt;CodeQL&lt;/a&gt;. Public repositories get CodeQL scanning by default. For private repositories, full CodeQL integration—security alerts, dashboards, and UI support—requires GitHub Advanced Security, which introduces additional cost and setup. CodeQL analysis is deep and reliable, but it prioritizes correctness over speed and is not optimized for fast, conversational PR feedback like AI-first reviewers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: Teams that value stability and minimal workflow disruption, and that prefer to stay within the existing GitHub ecosystem. If adopting new tools or changing review workflows is a hard sell, GitHub’s native features are often “good enough” for baseline review automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notable limitation&lt;/strong&gt;: Merge queue availability is restricted to higher-tier GitHub plans and is not universally accessible. Many teams still merge PRs sequentially and run CI per-PR rather than benefiting from batch testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 3: The "Comment Bots"
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lwjlluuhem07ef0krfk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lwjlluuhem07ef0krfk.png" alt="CodeRabbit" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool: CodeRabbit
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.coderabbit.ai" rel="noopener noreferrer"&gt;CodeRabbit&lt;/a&gt; is a feature-rich AI code reviewer that operates entirely within pull request comments. It combines large language models with a broad set of linters and security scanners to generate comprehensive feedback on code changes. In addition to identifying issues, it can generate PR summaries, create sequence diagrams, suggest fixes, and propose test cases.&lt;/p&gt;

&lt;p&gt;The breadth of analysis is both its main strength and its primary trade-off. CodeRabbit surfaces a wide range of findings, but this often includes stylistic feedback and minor suggestions alongside substantive issues. As a result, teams frequently invest time in tuning configuration and rules to reduce noise and focus the output on what matters most for their codebase.&lt;/p&gt;

&lt;p&gt;CodeRabbit integrates with major source control platforms, including GitHub, GitLab, Bitbucket, and Azure DevOps. It also provides a VS Code extension that enables pre-PR review, allowing developers to catch issues before opening a pull request. This broad platform support makes it suitable for organizations operating across multiple repositories or hosting providers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: Open source maintainers or teams that want extensive automated feedback directly in their existing PR workflow, without introducing a new review interface. Its multi-platform support is particularly valuable for organizations that are not GitHub-only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;: Offers a free tier with basic features such as PR summarization. Paid plans are approximately $24–$30 per user per month (Pro), depending on billing, and unlock advanced analysis and customization options.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 4: The "Deep Context" Engine
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8edxn7v7rkesmpuzj9i6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8edxn7v7rkesmpuzj9i6.png" alt="Greptile" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Tool: Greptile
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.greptile.com" rel="noopener noreferrer"&gt;Greptile&lt;/a&gt; takes a fundamentally different approach. Instead of analyzing just the PR diff, it builds a full graph of your codebase—understanding how functions connect, where code is used, and how similar patterns have evolved over time.&lt;/p&gt;

&lt;p&gt;This deep context means Greptile can catch issues that span multiple files or reference historical changes. It's like having a reviewer who has memorized your entire repository. The trade-off is complexity: Greptile requires more setup and resources than a simple diff-based tool.&lt;/p&gt;

&lt;p&gt;Greptile emphasizes enterprise features: self-hosted deployment, custom AI models, SOC 2 compliance, and support for both GitHub and GitLab. You can run it on-premises if your security team requires it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: Large codebases where understanding cross-module dependencies is critical. If you're working on a system with millions of lines of code and complex architectural patterns, Greptile's full-repo analysis catches issues that simpler tools miss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;: ~$30/user/month (cloud), enterprise negotiation for on-prem. 100% off for open-source and 50% off for startups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Category 5: The "Pre-Merge" Hunter
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx68ycq975jfhx08xsuik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx68ycq975jfhx08xsuik.png" alt="BugBot" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool: BugBot (by Cursor)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cursor.com/docs/bugbot" rel="noopener noreferrer"&gt;Bugbot&lt;/a&gt; is a narrowly focused AI code review tool designed to identify logic bugs and security issues with a low false-positive rate. Rather than acting as a full code review or workflow platform, it concentrates on surfacing concrete, actionable problems in code changes.&lt;/p&gt;

&lt;p&gt;Bugbot primarily operates on GitHub pull requests. When a PR is opened, it automatically reviews the diff and leaves comments highlighting potential bugs, edge cases, or security concerns. For users of the Cursor IDE, flagged issues can be opened directly in the editor, where suggested fixes can be applied or modified before committing updates.&lt;/p&gt;

&lt;p&gt;The tool is optimized for depth over breadth. It intentionally avoids stylistic feedback and general code quality suggestions, focusing instead on issues that are likely to cause incorrect behavior or security risk. Bugbot also supports custom review rules, allowing teams to encode project-specific constraints and conventions.&lt;/p&gt;

&lt;p&gt;The primary limitation is scope. Bugbot does not aim to replace a full code review platform: it does not generate PR summaries, manage approvals, or integrate with project management workflows. Its role is confined to automated detection of substantive bugs within pull requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for&lt;/strong&gt;: Individual contributors or teams who want automated, high-signal bug detection during PR review—especially those already using Cursor. It can be particularly useful in environments where logic or security defects carry a high operational cost.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing&lt;/strong&gt;: Bugbot offers a limited free tier for Cursor Teams and Individual plan users. Paid plans are approximately $40 per user per month, depending on usage limits and team features, and are available as add-ons to Cursor plans.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Graphite&lt;/th&gt;
&lt;th&gt;GitHub&lt;/th&gt;
&lt;th&gt;CodeRabbit&lt;/th&gt;
&lt;th&gt;Greptile&lt;/th&gt;
&lt;th&gt;BugBot&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GitHub App + CLI&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;GitHub App&lt;/td&gt;
&lt;td&gt;GitHub App + API&lt;/td&gt;
&lt;td&gt;GitHub App&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Focus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Workflow + AI&lt;/td&gt;
&lt;td&gt;Manual Review&lt;/td&gt;
&lt;td&gt;PR Comments&lt;/td&gt;
&lt;td&gt;Deep Context&lt;/td&gt;
&lt;td&gt;PR Comments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Analysis Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PR diff + context&lt;/td&gt;
&lt;td&gt;PR diff&lt;/td&gt;
&lt;td&gt;PR diff + linters&lt;/td&gt;
&lt;td&gt;Full repo graph&lt;/td&gt;
&lt;td&gt;Current PR + existing PR comments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Noise Level&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very Low (~5%)&lt;/td&gt;
&lt;td&gt;Varies&lt;/td&gt;
&lt;td&gt;Medium-High&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stacked PRs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Native&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Merge Queue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Enterprise only&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pricing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;$20-40/user&lt;/td&gt;
&lt;td&gt;Included + Copilot&lt;/td&gt;
&lt;td&gt;$12-30/user&lt;/td&gt;
&lt;td&gt;~$30/user&lt;/td&gt;
&lt;td&gt;Limited usage included with Cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Which Tool Fits Your Stack?
&lt;/h2&gt;

&lt;p&gt;There's no universal winner. The right choice depends on where your team is blocked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If your bottleneck is the review process itself&lt;/strong&gt;—PRs sit too long, developers get blocked, context-switching kills momentum—Graphite's workflow improvements matter more than any AI feature. The stacked PRs and merge queue can cut review time from days to hours, even before the AI comments help.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're fully invested in GitHub and Microsoft tools&lt;/strong&gt;, the native features are adequate. You won't get workflow improvements, but you also won't need to onboard a new platform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you need cross-platform support&lt;/strong&gt; or want comprehensive coverage including linters and security scanners, CodeRabbit delivers. Be prepared to tune its configuration to reduce noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're working on a massive legacy codebase&lt;/strong&gt; where understanding historical context is critical, Greptile's full-repo analysis is worth the complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you're already using Cursor IDE&lt;/strong&gt; and want to catch bugs before they are merged, BugBot is the most focused solution.&lt;/p&gt;

&lt;p&gt;The real question isn't "which AI reviewer is best?" It's "what's actually slowing down my team's shipping velocity?" Answer that first, then pick the tool that addresses it.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What are you running in production?&lt;/strong&gt; Are you trusting AI to auto-approve yet, or strictly using it for summaries? I'm curious what everyone else is seeing. Drop a comment below.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codereview</category>
      <category>coding</category>
      <category>api</category>
    </item>
    <item>
      <title>How to Stack Pull Requests on GitHub (Without Losing Your Mind)</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Fri, 16 Jan 2026 12:25:13 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/how-to-stack-pull-requests-on-github-without-losing-your-mind-54p8</link>
      <guid>https://dev.to/heraldofsolace/how-to-stack-pull-requests-on-github-without-losing-your-mind-54p8</guid>
      <description>&lt;p&gt;You just finished building your feature. You open a pull request. Then you wait. And wait. And wait some more.&lt;/p&gt;

&lt;p&gt;Meanwhile, the next part of your project is ready to build, but it depends on the code you just submitted. Do you start working anyway and risk a rebasing nightmare? Or do you context switch to something completely different and lose your flow?&lt;/p&gt;

&lt;p&gt;This is the "waiting game" that kills productivity. You've probably tried both approaches. Starting the next feature while the first is still under review leads to merge conflicts when changes come back. Context switching to unrelated work destroys your momentum and makes you lose the mental model you built up.&lt;/p&gt;

&lt;p&gt;The typical escape hatch is cramming everything into one massive 1,000-line PR. Ship it all at once, avoid branch dependencies entirely. But reviewers hate this. Big PRs take forever to review. Bugs hide in the noise. And if something needs to change, you're back to square one.&lt;/p&gt;

&lt;p&gt;There's a better way. Stacked pull requests let you break work into small, reviewable chunks without the Git gymnastics. And the Graphite CLI (&lt;code&gt;gt&lt;/code&gt;) makes it stupid simple.&lt;/p&gt;

&lt;p&gt;This isn't theory. Companies like Airbnb and Meta have used stacking for years. Now it's available for the rest of us, without building custom tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why stacking matters
&lt;/h2&gt;

&lt;p&gt;When you stack PRs, you create a chain: PR 1 builds the foundation, PR 2 builds on PR 1, and PR 3 builds on PR 2. Each PR is small and focused. Reviewers can approve them independently. You keep coding without getting blocked.&lt;/p&gt;

&lt;p&gt;Think of it like chapters in a book. Each chapter makes sense on its own, but they build toward a larger story. Your reviewer reads chapter 1 (the API), approves it, then moves to chapter 2 (the UI). They're not overwhelmed by 50 pages at once.&lt;/p&gt;

&lt;p&gt;The benefits compound:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster reviews&lt;/strong&gt;: A 200-line PR gets reviewed in minutes. A 2,000-line PR sits for days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better feedback&lt;/strong&gt;: Reviewers spot issues when the context is fresh and focused.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easier debugging&lt;/strong&gt;: When something breaks, you know exactly which small change caused it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No blocking&lt;/strong&gt;: You keep building while waiting for reviews.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem? Managing this in vanilla Git is painful. Rebasing changes down the stack, keeping base branches in sync, and avoiding merge conflicts requires constant mental overhead. You spend more time managing branches than writing code.&lt;/p&gt;

&lt;p&gt;Graphite automates all of it. The entire workflow becomes three commands: create, submit, restack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup in 60 seconds
&lt;/h2&gt;

&lt;p&gt;Install the Graphite CLI using either Homebrew or npm:&lt;/p&gt;

&lt;p&gt;brew installation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;withgraphite/tap/graphite
gt &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;npm installation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @withgraphite/graphite-cli@stable
gt &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sign in to &lt;a href="https://app.graphite.com/activate" rel="noopener noreferrer"&gt;https://app.graphite.com/activate&lt;/a&gt; with your GitHub account. You might be prompted to complete the setup if you haven’t done so already. There, you can create a token, and Graphite will provide you with an authentication command to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt auth &lt;span class="nt"&gt;--token&lt;/span&gt; &amp;lt;your_cli_auth_token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this command, which will log you in.&lt;/p&gt;

&lt;p&gt;Initialize Graphite in your repo (make sure this repo exists in GitHub, and you chose it for syncing during Graphite setup):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Creates local config, doesn't modify your actual repo&lt;/span&gt;
gt init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Graphite works alongside Git. You can still use &lt;code&gt;git status&lt;/code&gt;, &lt;code&gt;git add&lt;/code&gt;, and other standard Git commands. The &lt;code&gt;gt&lt;/code&gt; commands just handle the branching logic for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the first layer: backend API
&lt;/h2&gt;

&lt;p&gt;Let's build a simple todo app. Start with the API endpoints.&lt;/p&gt;

&lt;p&gt;Make sure you’re on the main branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt checkout main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now write your code. Here's a minimal Express API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// In-memory storage for todos&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="c1"&gt;// GET endpoint to fetch all todos&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// POST endpoint to create a new todo&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server running on port 3000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now run the following command, which will create a new branch named &lt;code&gt;feat/api&lt;/code&gt; and add a commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt create feat/api &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"Add API endpoints"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API is done, but not merged yet. Time to stack the frontend on top.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stacking the frontend: the magic part
&lt;/h2&gt;

&lt;p&gt;Here's where it gets interesting. The API isn't approved or merged, but you're ready to build the UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- index.html --&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Todo App&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;My Todos&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todo-input"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Add a todo"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"addTodo()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Add&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"todo-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Fetch and display all todos on page load&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/li&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Add a new todo via POST request&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo-input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/todos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;loadTodos&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Refresh the list&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;loadTodos&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Initialize on page load&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this command, which creates a &lt;code&gt;feat/ui&lt;/code&gt; branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt create feat/ui &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"Add UI"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Graphite automatically sets &lt;code&gt;feat/api&lt;/code&gt; as the parent. You're now working on code that depends on unmerged changes.&lt;/p&gt;

&lt;p&gt;You now have two branches, stacked on top of each other, both ready to review. You can check the structure with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gt log short

◉  feat/ui
◯  feat/api
◯  main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Syncing to GitHub: one command
&lt;/h2&gt;

&lt;p&gt;Push everything to GitHub and create PRs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt submit &lt;span class="nt"&gt;--stack&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Graphite does three things automatically:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Detects both branches in your stack&lt;/li&gt;
&lt;li&gt;Creates two separate PRs on GitHub&lt;/li&gt;
&lt;li&gt;Sets the base branch correctly so diffs show only the new changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On running the command, you’ll be taken to a webpage, where you can add a description for the PRs, and publish them.&lt;/p&gt;

&lt;p&gt;On GitHub, the PR for the UI is based on the &lt;code&gt;feat/api&lt;/code&gt; branch, not &lt;code&gt;main&lt;/code&gt;. This means reviewers see only the UI changes, not the API code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling review feedback: the restack
&lt;/h2&gt;

&lt;p&gt;Your reviewer asks you to rename a variable in the API. In a normal Git workflow, this breaks everything. You'd need to manually rebase the UI branch and pray you don't hit conflicts.&lt;/p&gt;

&lt;p&gt;With Graphite, it's automatic.&lt;/p&gt;

&lt;p&gt;Check out the API branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt checkout feat/api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Update variable name in server.js&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;todoList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt; &lt;span class="c1"&gt;// was 'todos'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit the changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt modify &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will amend the last commit to add the new changes. If you prefer to create a separate commit, you can use this command instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt modify &lt;span class="nt"&gt;--commit&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--message&lt;/span&gt; &lt;span class="s2"&gt;"Responded to reviewer feedback"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Graphite detects the change and automatically restacks (rebases) &lt;code&gt;feat/ui&lt;/code&gt; on top of the updated &lt;code&gt;feat/api&lt;/code&gt; code. No conflict resolution needed. No manual rebasing. It just works.&lt;/p&gt;

&lt;p&gt;Check your stack again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt log short
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both branches are in sync. The UI branch has the latest API changes.&lt;/p&gt;

&lt;p&gt;Resubmit the PR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt submit &lt;span class="nt"&gt;--stack&lt;/span&gt; &lt;span class="nt"&gt;--update-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pro tips for power users
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Visualize your stack.&lt;/strong&gt; Use &lt;code&gt;gt log short&lt;/code&gt; anytime to see where you are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt log short
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows your branch hierarchy at a glance. It's like &lt;code&gt;git log&lt;/code&gt;, but focused on your stack structure instead of commit history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sync frequently.&lt;/strong&gt; Start each work session with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt &lt;span class="nb"&gt;sync&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pulls the latest changes from main, detects any merged PRs, and prompts you to delete local branches that are no longer needed. It keeps your stack clean.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're not locked in.&lt;/strong&gt; Merge PRs directly on GitHub if you want. Graphite figures it out. You can mix and match workflows. Some team members can use Graphite while others stick with vanilla Git.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your teammates don't need Graphite.&lt;/strong&gt; They review PRs on GitHub like normal. Graphite only changes your local workflow, not theirs. The PRs look identical to standard GitHub PRs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Navigate with commands.&lt;/strong&gt; Use &lt;code&gt;gt up&lt;/code&gt; and &lt;code&gt;gt down&lt;/code&gt; to move between branches in your stack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Move to the parent branch&lt;/span&gt;
gt down

&lt;span class="c"&gt;# Move to the child branch  &lt;/span&gt;
gt up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is faster than typing out branch names, especially when you're deep in a stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Split changes later.&lt;/strong&gt; If you realize mid-branch that you should split the work, use &lt;code&gt;gt split&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt &lt;span class="nb"&gt;split&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Graphite will guide you through splitting commits into separate branches. Useful when you realize a "quick fix" turned into a bigger change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Amend instead of adding commits.&lt;/strong&gt; Instead of creating multiple commits on one PR, amend your work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gt modify &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps each branch as a single, clean commit. When you need to address review feedback, amend again. The history stays linear and readable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Stacking lets you code at the speed of thought, not the speed of CI. Small PRs get reviewed faster. Fewer bugs slip through. You stay in flow instead of waiting around.&lt;/p&gt;

&lt;p&gt;GitHub's review process is powerful, but it wasn't built for this workflow. Graphite fills the gap. It's like adding power steering to your development process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://graphite.com/docs/install-the-cli" rel="noopener noreferrer"&gt;Graphite CLI Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stacking.dev" rel="noopener noreferrer"&gt;Stacking.dev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://graphite.com/docs" rel="noopener noreferrer"&gt;Graphite Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you tried stacking before? Drop a comment and let me know if you prefer big PRs or small stacks.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>git</category>
      <category>github</category>
      <category>graphite</category>
    </item>
    <item>
      <title>The 6 Best AI Code Review Tools for Pull Requests in 2025</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Fri, 12 Dec 2025 16:11:53 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/the-6-best-ai-code-review-tools-for-pull-requests-in-2025-4n43</link>
      <guid>https://dev.to/heraldofsolace/the-6-best-ai-code-review-tools-for-pull-requests-in-2025-4n43</guid>
      <description>&lt;p&gt;&lt;em&gt;A comprehensive comparison of platforms, bots, and agents to speed up your code review cycle.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction: The "Review Gap" in the Age of AI&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AI code generation tools like GitHub Copilot and Cursor have fundamentally changed how developers write code. What once took hours now takes minutes. A single developer can output 2-3x more code than before. But here's the problem I keep seeing with teams I talk to: while AI has accelerated code creation, human review capacity has remained completely flat.&lt;/p&gt;

&lt;p&gt;This mismatch has created what I call the "Review Gap." Your team is writing more code than ever, but you're still reviewing it the old way, one PR at a time, with the same limited human attention. Pull requests pile up. Context switching increases. Deployment velocity suffers.&lt;/p&gt;

&lt;p&gt;The solution isn't to hire more reviewers or work longer hours. The solution is to apply AI to the review process itself. Modern AI code review tools have evolved far beyond simple linters. They provide context-aware analysis that can summarize changes, catch subtle bugs, verify architectural alignment, and even suggest fixes, all in seconds rather than hours.&lt;/p&gt;

&lt;p&gt;But not all AI review tools are created equal. I tested the leading options based on three critical criteria:&lt;/p&gt;

&lt;p&gt;Context awareness: Does it understand your entire repository, or just the lines that changed?&lt;/p&gt;

&lt;p&gt;False positive rate: Does it waste your time with nitpicks, or does it flag genuinely important issues?&lt;/p&gt;

&lt;p&gt;Workflow integration: Does it fit naturally into your development process, or is it just another noisy bot cluttering your PR timeline?&lt;/p&gt;

&lt;p&gt;Let's look at the six best tools available in 2025.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff33zr82ab5689w22by83.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff33zr82ab5689w22by83.png" alt="Graphite Agent" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Graphite Agent (Best Overall Platform)
&lt;/h2&gt;

&lt;p&gt;Category: Comprehensive review platform&lt;/p&gt;

&lt;p&gt;Why it wins: Graphite solves the root cause of slow reviews (workflow) and applies AI on top.&lt;/p&gt;

&lt;p&gt;Most AI code review tools are bots that bolt onto your existing GitHub workflow. They leave comments, generate summaries, and hope for the best. &lt;a href="https://graphite.dev/" rel="noopener noreferrer"&gt;Graphite&lt;/a&gt; takes a fundamentally different approach. It's a platform that rethinks the entire code review workflow, starting with how you structure your changes.&lt;/p&gt;

&lt;p&gt;Graphite's killer feature is stacked pull requests. Instead of creating one massive PR with 2,000 lines of changes, you break your work into small, atomic PRs that build on each other. Each PR in the stack is focused and reviewable. This approach dramatically improves review speed and quality, but here's the AI advantage: smaller PRs give AI dramatically better results.&lt;/p&gt;

&lt;p&gt;When Graphite Agent reviews a 200-line PR with a clear scope, it can provide genuinely useful feedback. It catches type errors, identifies potential race conditions, spots security vulnerabilities, and suggests optimizations. When competitors try to review a 2,000-line monolithic PR, their AI struggles with context and produces generic advice or misses critical issues entirely.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphite.dev/features/agent" rel="noopener noreferrer"&gt;Graphite Agent&lt;/a&gt; isn't just a comment bot. It's an interactive companion that lives on your PR page. You can ask it questions like "What happens if this API endpoint receives a null value?" or "Is this change thread-safe?" It generates test plans, explains complex logic, and provides instant summaries of what changed and why.&lt;/p&gt;

&lt;p&gt;The integration feels seamless. Graphite Agent appears directly in Graphite's PR inbox, which provides a cleaner, faster interface than GitHub's native UI. Reviews happen in under 90 seconds. The AI maintains a sub-5% negative feedback rate, meaning developers trust its suggestions because they're rarely wrong.&lt;/p&gt;

&lt;p&gt;Pricing is straightforward and team-friendly. Graphite Agent is included in paid plans. The Team plan runs around $40 per user per month with unlimited AI reviews, making it cost-effective for organizations that review hundreds of PRs weekly.&lt;/p&gt;

&lt;p&gt;Best for: Teams who want to fundamentally speed up their development velocity, not just add a bot to GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13vqglizjoyqwcu4uktr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F13vqglizjoyqwcu4uktr.png" alt="GitHub Copilot" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. GitHub Copilot (Best for IDE Integration)
&lt;/h2&gt;

&lt;p&gt;Category: IDE and native GitHub integration&lt;/p&gt;

&lt;p&gt;Overview: The default choice for many teams. GitHub Copilot excels at "in-the-flow" assistance while writing code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt; started as an autocomplete tool but has expanded into code review territory. If you're already using VS Code with Copilot, you get basic PR review features built in. Copilot can generate PR descriptions, summarize changes, and leave inline comments on pull requests through GitHub's native interface (especially with the Enterprise tier).&lt;/p&gt;

&lt;p&gt;The main advantage is deep integration with the Microsoft ecosystem. Copilot works seamlessly in VS Code, integrates with GitHub Enterprise, and meets enterprise compliance requirements out of the box. For organizations heavily invested in Microsoft tooling, this integration is valuable. You get a single vendor, unified billing, and consistent security policies across development tools.&lt;/p&gt;

&lt;p&gt;The "Copilot Workspace" feature allows developers to describe desired changes in natural language, and Copilot suggests code modifications. This works well for small refactoring tasks or implementing simple features. The AI has improved significantly over the past year and can handle increasingly complex requests.&lt;/p&gt;

&lt;p&gt;That said, Copilot's PR review experience has notable limitations compared to specialized agents. The analysis often stays surface-level, focused on style and obvious bugs rather than architectural concerns or subtle logic errors. Early adopters report that Copilot's PR comments can be noisy if not carefully configured. The AI doesn't have the same "agentic" feel as specialized tools. It provides suggestions but lacks the deeply interactive, conversational interface that helps developers dig deeper into complex issues on the PR timeline itself.&lt;/p&gt;

&lt;p&gt;Pricing varies by plan. GitHub Copilot Individual is $10 per month, while Copilot Business is $19 per user per month. Organizations paying for GitHub Enterprise receive the most advanced PR summarization and review features as part of their contract.&lt;/p&gt;

&lt;p&gt;Best for: Teams fully committed to the Microsoft ecosystem who want a safe, baseline AI experience without changing their workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nyoon9md5pg4c2g8fp8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3nyoon9md5pg4c2g8fp8.png" alt="CodeRabbit" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. CodeRabbit (Best Standalone Bot)
&lt;/h2&gt;

&lt;p&gt;Category: Third-party PR bot&lt;/p&gt;

&lt;p&gt;Overview: A popular tool that connects to GitHub, GitLab, or Bitbucket and provides detailed AI reviews through PR comments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://coderabbit.ai/" rel="noopener noreferrer"&gt;CodeRabbit&lt;/a&gt; has established itself as the leading third-party AI review bot. It offers a rich feature set that goes well beyond basic static analysis. When you open a PR, CodeRabbit automatically generates a detailed "walkthrough" summary explaining what changed and why. It uses a combination of large language models and traditional linters (it runs over 40 different code analyzers) to provide comprehensive feedback.&lt;/p&gt;

&lt;p&gt;The chat interface is a standout feature. You can have a conversation with CodeRabbit directly in PR comments, asking follow-up questions or requesting clarification on its suggestions. This makes the review process more interactive and helps developers understand the reasoning behind each recommendation.&lt;/p&gt;

&lt;p&gt;CodeRabbit is highly configurable. You can tune its "nitpickiness" level, define custom rules for your codebase, and train it to learn from your team's feedback over time. It integrates with multiple platforms (GitHub, GitLab, Bitbucket, Azure DevOps) and offers both cloud and self-hosted deployment options for teams with strict security requirements.&lt;/p&gt;

&lt;p&gt;The main drawback is noise. CodeRabbit leaves many comments on each PR. While comprehensive analysis can be valuable, it also clutters the GitHub timeline and can overwhelm developers, especially on larger changes. You'll need to invest time in configuration to dial down irrelevant feedback. Some teams report that CodeRabbit's enthusiasm for suggesting improvements, while well-intentioned, creates review fatigue.&lt;/p&gt;

&lt;p&gt;Pricing is competitive. CodeRabbit offers a free tier for open-source projects and personal use. Paid plans range from $12 per user per month(Lite) to $24-$30 per user per month (Pro). The Pro tier includes all features: advanced linters, chat capabilities, and detailed reporting.&lt;/p&gt;

&lt;p&gt;Best for: Teams who want to keep the native GitHub UI but need better automated feedback, and who don't mind investing time in configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93zauvxzfdy3a0ts9zcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93zauvxzfdy3a0ts9zcb.png" alt="Greptile" width="800" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Greptile (Best for Deep Context and RAG)
&lt;/h2&gt;

&lt;p&gt;Category: Specialized context engine&lt;/p&gt;

&lt;p&gt;Overview: Focuses heavily on understanding your entire codebase, not just the diff in each PR.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://greptile.com/" rel="noopener noreferrer"&gt;Greptile&lt;/a&gt; takes a unique approach to AI code review by building a comprehensive knowledge graph of your entire repository. It indexes every function, every dependency, every historical change, and uses this context to provide unusually deep analysis. When reviewing a PR, Greptile doesn't just look at the changed lines. It understands how those changes ripple through your entire codebase.&lt;/p&gt;

&lt;p&gt;This approach excels at answering complex questions that require cross-file or cross-module understanding. For example: "How does this API change affect the billing service?" or "What other components depend on this function?" Greptile can trace dependencies, identify potential breaking changes, and spot issues that would be nearly impossible for a human reviewer to catch without hours of investigation.&lt;/p&gt;

&lt;p&gt;The tool is particularly valuable for large, legacy codebases where understanding context is the hardest part of code review. New team members benefit enormously from Greptile's ability to explain how different parts of the system interact. Senior engineers appreciate having an AI that can flag subtle architectural issues that simple diff-based analysis would miss.&lt;/p&gt;

&lt;p&gt;Greptile emphasizes customization and enterprise features. You can define private AI models, create custom rules specific to your organization, and deploy Greptile on-premises for complete control over your data. The tool meets SOC 2 compliance standards and integrates with both GitHub and GitLab.&lt;/p&gt;

&lt;p&gt;The trade-off is complexity. Greptile's full-repository analysis takes time to set up and requires more computational resources than simpler diff-based tools. The platform is priced for enterprise use at around $30 per user per month for cloud deployment, with custom pricing for self-hosted options. For smaller teams or simpler codebases, this may be more powerful than necessary.&lt;/p&gt;

&lt;p&gt;Best for: Large, complex monorepos where understanding the impact of changes across the entire system is the hardest part of code review.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0d531dwkt9fcptj0tuvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0d531dwkt9fcptj0tuvs.png" alt="Ellipsis" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Ellipsis (Best for Automated Fixes)
&lt;/h2&gt;

&lt;p&gt;Category: Action-oriented agent&lt;/p&gt;

&lt;p&gt;Overview: An AI agent that can take reviewer comments and automatically implement the requested changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ellipsis.dev/" rel="noopener noreferrer"&gt;Ellipsis&lt;/a&gt; bridges the gap between review and implementation. Most AI review tools identify issues and leave comments. Ellipsis goes further: it can read a reviewer's comment ("Make this variable const" or "Add input validation here") and automatically generate a commit with the fix.&lt;/p&gt;

&lt;p&gt;This capability is genuinely useful for reducing the back-and-forth in code reviews. Instead of the original author context-switching back to their IDE, finding the file, making the change, and pushing a new commit, Ellipsis handles it automatically. For minor refactoring tasks, style fixes, or simple logic adjustments, this saves significant time.&lt;/p&gt;

&lt;p&gt;The tool works by maintaining a detailed understanding of your codebase and coding standards. When it receives a request to implement a change, it generates the code, runs tests to verify nothing breaks, and commits the result. You can review and approve the change before it's applied, maintaining control over what gets merged.&lt;/p&gt;

&lt;p&gt;Ellipsis integrates with GitHub and supports multiple programming languages. The AI is trained on millions of open-source repositories, giving it broad knowledge of common patterns and best practices. It's particularly effective for teams that follow consistent coding conventions, as the AI can learn and replicate those patterns.&lt;/p&gt;

&lt;p&gt;The limitations are predictable: Ellipsis handles simple, mechanical changes well but struggles with complex logic or architectural refactoring. It works best as a junior engineer who can take clear direction and implement straightforward fixes. For ambiguous or high-complexity changes, human implementation is still necessary.&lt;/p&gt;

&lt;p&gt;Pricing has recently shifted to a seat-based model. Ellipsis charges $20 per user per month for unlimited usage, moving away from previous per-commit pricing structures. This makes it predictable for teams to adopt.&lt;/p&gt;

&lt;p&gt;Best for: Teams who spend too much time on minor refactoring cycles and want to automate the implementation of simple reviewer feedback.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oug1e5ld8x5foh63fm9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1oug1e5ld8x5foh63fm9.png" alt="BugBot" width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. BugBot by Cursor (Best for Security and Logic Errors)
&lt;/h2&gt;

&lt;p&gt;Category: Specialized defect detection&lt;/p&gt;

&lt;p&gt;Overview: A tool trained specifically to find logic bugs and security vulnerabilities, developed as part of the Cursor ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cursor.com/bugbot" rel="noopener noreferrer"&gt;BugBot&lt;/a&gt; takes a surgical approach to code review. Instead of trying to do everything, it focuses exclusively on finding critical bugs and security issues. It's designed to act as a "pre-merge safety net" that catches hard-to-spot problems before they reach production.&lt;/p&gt;

&lt;p&gt;The tool excels at identifying edge cases, race conditions, null pointer exceptions, and security vulnerabilities. It's particularly effective at reviewing AI-generated code, where subtle logic errors are more common than with human-written code. BugBot's analysis is highly precise. It maintains a low false-positive rate by focusing only on genuinely problematic code rather than style issues or minor optimizations.&lt;/p&gt;

&lt;p&gt;BugBot is deeply integrated with the &lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;Cursor IDE&lt;/a&gt;. When it flags an issue, you can jump directly to the problematic code in your editor and apply the suggested fix with one click. The tight IDE integration makes the review-fix-verify cycle extremely fast.&lt;/p&gt;

&lt;p&gt;The main limitation is scope. BugBot doesn't generate PR summaries, doesn't provide architectural feedback, and doesn't help with code documentation or readability. It has one job: find critical bugs. For teams that need comprehensive review assistance, BugBot would need to be paired with other tools.&lt;/p&gt;

&lt;p&gt;Adoption requires committing to the Cursor ecosystem. If your team uses VS Code, IntelliJ, or other editors, switching to Cursor represents a significant change. Some developers love Cursor's AI-first features, others prefer their existing tools.&lt;/p&gt;

&lt;p&gt;Pricing is currently tied to your Cursor subscription. It's generally included in Cursor's paid plans (Pro/Business) which start at $20 per user per month, rather than being sold as a standalone bot.&lt;/p&gt;

&lt;p&gt;Best for: High-compliance industries or mission-critical codebases where preventing logic errors and security vulnerabilities is paramount, and teams willing to adopt the Cursor IDE.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Comparison Table: At a Glance&lt;/strong&gt;
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Primary Focus&lt;/th&gt;
&lt;th&gt;Context Awareness&lt;/th&gt;
&lt;th&gt;Pricing Model&lt;/th&gt;
&lt;th&gt;Best Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Graphite Agent&lt;/td&gt;
&lt;td&gt;Complete platform (workflow + AI)&lt;/td&gt;
&lt;td&gt;PR diff + relevant context&lt;/td&gt;
&lt;td&gt;$40/user/month&lt;/td&gt;
&lt;td&gt;Teams wanting to fundamentally improve velocity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Copilot&lt;/td&gt;
&lt;td&gt;IDE integration + basic PR review&lt;/td&gt;
&lt;td&gt;Limited to diff&lt;/td&gt;
&lt;td&gt;$19/user/month (Business)&lt;/td&gt;
&lt;td&gt;Microsoft ecosystem commitment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CodeRabbit&lt;/td&gt;
&lt;td&gt;Comprehensive PR bot&lt;/td&gt;
&lt;td&gt;PR diff + linters&lt;/td&gt;
&lt;td&gt;$12-30/user/month&lt;/td&gt;
&lt;td&gt;Teams keeping native GitHub UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Greptile&lt;/td&gt;
&lt;td&gt;Deep codebase analysis (RAG)&lt;/td&gt;
&lt;td&gt;Full repository graph&lt;/td&gt;
&lt;td&gt;$30/user/month&lt;/td&gt;
&lt;td&gt;Large monorepos, complex dependencies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ellipsis&lt;/td&gt;
&lt;td&gt;Automated fix implementation&lt;/td&gt;
&lt;td&gt;PR diff + coding standards&lt;/td&gt;
&lt;td&gt;$20/user/month&lt;/td&gt;
&lt;td&gt;Reducing refactoring cycles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BugBot&lt;/td&gt;
&lt;td&gt;Critical bug detection&lt;/td&gt;
&lt;td&gt;PR diff focused on logic/security&lt;/td&gt;
&lt;td&gt;Included in Cursor Sub ($20+)&lt;/td&gt;
&lt;td&gt;High-compliance, mission-critical code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How to Choose the Right Tool&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For pure speed and workflow improvement: Choose Graphite. The combination of stacked PRs and integrated AI provides the fastest path from code complete to merge. You're not just adding AI to a slow process, you're fixing the process itself.&lt;/p&gt;

&lt;p&gt;For massive monorepo querying: Choose Greptile. If your biggest challenge is understanding how changes ripple through a large, complex codebase, Greptile's full-repository analysis provides unmatched context.&lt;/p&gt;

&lt;p&gt;For simple summaries on GitHub: Choose CodeRabbit or GitHub Copilot. If you want to keep your existing workflow and just add AI feedback, either of these bots will provide useful suggestions without requiring major changes to how your team works.&lt;/p&gt;

&lt;p&gt;For mission-critical bug prevention: Choose BugBot (Cursor). If you're in a regulated industry or working on code where bugs have severe consequences, BugBot's laser focus on critical defects provides valuable protection.&lt;/p&gt;

&lt;p&gt;For reducing trivial back-and-forth: Choose Ellipsis. If your reviews are slow because of many small fix requests, having an AI that can implement those fixes automatically saves significant time.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AI code review tools are no longer optional. They're essential for handling the volume of code being generated today. The Review Gap is real, and it's growing. Teams that don't adopt AI assistance will find themselves increasingly unable to keep up with the pace of development.&lt;/p&gt;

&lt;p&gt;That said, not all AI tools address the underlying problem. Most are bots that add automation to a fundamentally slow, inefficient workflow. They help, but they're band-aids on a broken process.&lt;/p&gt;

&lt;p&gt;To truly fix code review, you need a platform that incentivizes better practices (smaller, more focused PRs) and reviews them with AI. That's why Graphite stands out. It's not just an AI reviewer, it's a complete rethinking of how code review should work in the age of AI-accelerated development.&lt;/p&gt;

&lt;p&gt;The best teams are shipping faster than ever while maintaining higher code quality. They're doing it by combining better workflow practices with AI assistance. If you're still relying on manual reviews of large, monolithic PRs, you're leaving velocity and quality on the table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphite.dev/" rel="noopener noreferrer"&gt;Try Graphite Agent today&lt;/a&gt; — it's included in every Graphite plan. Sign up and review your first stack in minutes.&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>programming</category>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>How to use Cursor to Generate API Testcases in Requestly</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Thu, 27 Nov 2025 14:02:16 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/how-to-use-cursor-to-generate-api-testcases-in-requestly-4pn</link>
      <guid>https://dev.to/heraldofsolace/how-to-use-cursor-to-generate-api-testcases-in-requestly-4pn</guid>
      <description>&lt;p&gt;API testing is one of those tasks every developer knows is essential, but few enjoy. Manually writing test cases for every endpoint is repetitive, error-prone, and consumes valuable time that could be spent building features. Edge cases are often skipped, test coverage suffers, and teams frequently find themselves maintaining brittle scripts.&lt;/p&gt;

&lt;p&gt;That’s where automation changes the game. By pairing &lt;a href="https://cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;, an AI-powered coding assistant, with &lt;a href="https://requestly.com" rel="noopener noreferrer"&gt;Requestly's&lt;/a&gt; local-first API testing and mocking platform, you can offload the grunt work of writing tests to AI while keeping execution secure and reproducible on your own system. In this article, we’ll walk through how to set up Cursor with Requestly, generate test cases automatically, and run them end-to-end so that you can focus less on boilerplate and more on shipping features.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Automate API Test Case Generation?
&lt;/h2&gt;

&lt;p&gt;Traditionally, writing API test cases involves manually scripting tests using the request and response schema. This process is slow, error-prone, and often results in incomplete coverage because developers don’t have time to test every possible scenario.&lt;/p&gt;

&lt;p&gt;The challenges include:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual scripting overhead:&lt;/strong&gt; Every endpoint and method must be coded by hand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incomplete coverage:&lt;/strong&gt; Edge cases and negative tests are frequently skipped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;High setup cost:&lt;/strong&gt; Establishing a reusable test framework requires a significant amount of time.&lt;/p&gt;

&lt;p&gt;This is where Cursor provides a significant advantage. Because it’s an AI-powered coding assistant, Cursor can quickly generate test cases based on endpoint definitions, documentation, or even just example payloads. It understands context and can suggest multiple variations, including edge and error scenarios, without requiring hours of manual coding.&lt;/p&gt;

&lt;p&gt;With Requestly's &lt;a href="https://docs.requestly.com/general/team/local-workspace" rel="noopener noreferrer"&gt;Local Workspaces&lt;/a&gt;, integrating Cursor becomes a breeze. Thanks to the local workspaces, all your requests are stored in the local filesystem as JSON files. For example, the following request:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cr42l7174gpbg8yvopw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cr42l7174gpbg8yvopw.png" alt="A sample test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;becomes a JSON file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{base_url}}/user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"preRequest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"postResponse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rq.test(&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Request is successful (2XX)&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, () =&amp;gt; {&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt; rq.response.to.be.ok&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;});"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"queryParams"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"contentType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text/plain"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"currentAuthType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INHERIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"authConfigStore"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Note that the post-response script is stored in the &lt;code&gt;scripts.postResponse&lt;/code&gt; key as a string.&lt;/p&gt;

&lt;p&gt;Unlike other Cloud-based tools like Postman, the local nature of Requestly means you can feed the files directly to Cursor, and Cursor can generate test cases and edit the files. These files can be checked into version control. With this setup, teams can automate the generation of API tests, and can collaborate and review the test files, thereby saving a huge amount of time.&lt;/p&gt;
&lt;h2&gt;
  
  
  API Testing with Requestly and Cursor
&lt;/h2&gt;

&lt;p&gt;As an example of a practical API to test, we'll be using the &lt;a href="https://docs.github.com/en/rest" rel="noopener noreferrer"&gt;GitHub REST API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To follow along with this tutorial, you'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt; installed on your computer.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token" rel="noopener noreferrer"&gt;GitHub Personal Access Token (PAT)&lt;/a&gt;. Choose a repository where you have write access and give it read-write access for issues.&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://requestly.com/downloads/" rel="noopener noreferrer"&gt;Requestly desktop app&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cursor.com/download" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Setting up Requestly
&lt;/h3&gt;

&lt;p&gt;I have already created a &lt;a href="https://github.com/heraldofsolace/requestly-demo" rel="noopener noreferrer"&gt;repo&lt;/a&gt; that contains a Requestly collection. Clone the repo to your machine.&lt;/p&gt;

&lt;p&gt;Start Requestly and click on the &lt;strong&gt;Workspaces&lt;/strong&gt; dropdown in the top-left corner and select &lt;strong&gt;+ Add&lt;/strong&gt;. Select the directory where you cloned the app, and write &lt;code&gt;requestly-demo&lt;/code&gt; as the name of the workspace. This will load the workspace of the same name that already exists in the directory.&lt;/p&gt;

&lt;p&gt;Once the workspace is ready, navigate to the &lt;strong&gt;APIs&lt;/strong&gt; tab, where you'll find the prepared &lt;strong&gt;GitHub API&lt;/strong&gt; collection. In the &lt;strong&gt;Environments&lt;/strong&gt; tab, you'll find an &lt;a href="https://docs.requestly.com/general/api-client/environments-and-variables/environment-variables" rel="noopener noreferrer"&gt;Environment&lt;/a&gt; named "Dev". Update the variables with your credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;token&lt;/code&gt;: Your PAT&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;owner&lt;/code&gt;: Your GitHub username&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;repo&lt;/code&gt;: The name of the repo that you chose&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Since the environments store the PAT in plaintext, It's recommended to add the &lt;code&gt;requestly-demo/environments&lt;/code&gt; directory to .gitignore. Or, you could use a pre-commit hook to remove the PAT from the environment JSON upon commiting.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Setting up Cursor Rules
&lt;/h3&gt;

&lt;p&gt;By default, Cursor can generate test cases for requests, since Requestly uses the commonly used &lt;a href="https://www.chaijs.com/" rel="noopener noreferrer"&gt;Chai.js&lt;/a&gt; syntax. However, Requestly adds a few syntactic sugars and abstractions on top of the Chai.js library. We can inform Cursor about these features and provide it with some instructions by creating a &lt;a href="https://cursor.com/docs/context/rules" rel="noopener noreferrer"&gt;project rule&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.cursor&lt;/code&gt; directory in the root of the project and create a file named &lt;code&gt;test-rules.mdc&lt;/code&gt; inside it with the following content. These instructions tell Cursor everything it needs to know for generating tests.&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;





&lt;p&gt;As you can see, we have iterated on some of the testing features provided by Requestly and have also provided some custom instructions. The &lt;code&gt;globs: requestly-demo/**/*.json&lt;/code&gt; line tells Cursor to automatically load this rule whenever you're working with one of the Requestly files.&lt;/p&gt;

&lt;p&gt;Since we're using the GitHub API, which is a well-documented public API, Cursor is already aware of the request and response structures. However, if you're testing your own API, it's a good idea to let Cursor know about your schemas by referencing your OpenAPI specification file. You can reference files in the rules by using &lt;code&gt;@filename&lt;/code&gt;. For example, you can add a line like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Use the OpenAPI specification for request and response structure: @schema.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generating Tests with Cursor
&lt;/h3&gt;

&lt;p&gt;Now that the Cursor rules are in place, let's test it out. Open the &lt;code&gt;requestly-demo/GitHub REST API/a862b625-82ce-4ad2-9c41-4cda55e1bb5e.json&lt;/code&gt; file, which creates an issue in your repo.&lt;/p&gt;

&lt;p&gt;Write "Generate test cases for this request. Show the test cases in this window before editing the file. Wait for my confirmation before editing the file" and hit enter.&lt;/p&gt;

&lt;p&gt;Cursor will generate a few test cases and show you in the chat window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipudjcvuvzxalkni8vzb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fipudjcvuvzxalkni8vzb.png" alt="The generated tests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a good idea to check that the test cases satisfy your expectations before writing them to the file. You can also ask it to add new tests, or change/remove any test you want. Once you're happy, ask it to update the file. Cursor will stringify the tests and write it in the &lt;code&gt;scripts.postResponse&lt;/code&gt; key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flue0ngqgy9t3vca6four.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flue0ngqgy9t3vca6four.png" alt="The tests are saved to the file"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accept the changes so that the file is updated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the Tests
&lt;/h3&gt;

&lt;p&gt;Go back to the Requestly Window and open the &lt;strong&gt;Create issue&lt;/strong&gt; request. Navigate to the &lt;strong&gt;Scripts&amp;gt; Post-response&lt;/strong&gt; tab, and you should see the tests Cursor just generated!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvu9r71mzdvq9t2wed8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbvu9r71mzdvq9t2wed8z.png" alt="The tests in Requestly"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: If the tests don't appear in Requestly, you may need to reload the workspace.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Click on &lt;strong&gt;Send&lt;/strong&gt; to run the tests. You should see the tests pass. Now you’ve got tests running without writing a single line by hand.&lt;/p&gt;

&lt;h3&gt;
  
  
  Iterate and Improve
&lt;/h3&gt;

&lt;p&gt;As with any AI tool, Cursor can make mistakes and may generate a faulty test. Additionally, your API can evolve over time, and your tests may need to be updated accordingly. Simply provide feedback to Cursor and ask it to update the tests. For example, in the tests Cursor generated for me, it included checks for various fields, such as &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt;, and so on. I can ask it to only check for the &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; fields.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fepmsrr2s0c8egzjp1pge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fepmsrr2s0c8egzjp1pge.png" alt="Updated instructions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The updated tests now only check for &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yz2blfx6rgiieqiuxop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yz2blfx6rgiieqiuxop.png" alt="The updated tests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, if the AI generates a faulty output (for example, a malformed JSON), you can prompt it to fix the mistake, and repeat this process until you get what you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scaling and Bulk Automation
&lt;/h3&gt;

&lt;p&gt;You saw how Cursor can help you generate and refine test cases. However, if you have a large number of endpoints, it's not feasible to repeat this process for each endpoint one by one. In this situation, you can use the &lt;a href="https://cursor.com/docs/cli/overview" rel="noopener noreferrer"&gt;Cursor CLI&lt;/a&gt; to generate tests in bulk. This process has the added benefit that you can integrate with a file watcher, like &lt;a href="https://emcrisostomo.github.io/fswatch/" rel="noopener noreferrer"&gt;fswatch&lt;/a&gt;, or a Git pre-commit hook to automatically generate tests whenever the request JSON is changed. Let's see this in action.&lt;/p&gt;

&lt;p&gt;After installing Cursor CLI, run the follwing command in the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cursor-agent &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"add tests to all the requestly files in requestly-demo/GitHub REST API/. Read instructions from .cursor/rules/test-rules.mdc"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cursor will now go ahead and add tests to all the requests.&lt;/p&gt;

&lt;p&gt;This process has a disadvantage that you won't be able to verify the tests before Cursor writes them to the file. So, be sure to check that the tests run properly before commiting them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for AI-Generated API Testing
&lt;/h2&gt;

&lt;p&gt;AI-assisted testing works best when you set it up for success. Cursor and Requestly make the workflow faster and safer, but the quality of your tests still depends on how you guide and maintain the process. Here are some best practices to keep your testing reliable over time:&lt;/p&gt;

&lt;h3&gt;
  
  
  Write Clear Prompts for Cursor
&lt;/h3&gt;

&lt;p&gt;Cursor will only be as precise as the instructions you provide. Don’t just say “write a test”. Instead, specify the fields you care about, the types of responses you expect, and whether you want edge or negative cases included. For example:&lt;/p&gt;

&lt;p&gt;"Generate tests for this request. Check for a 200 status, verify title and body fields exist, and add one negative test for unauthorized access."&lt;/p&gt;

&lt;p&gt;The more context you give, the less cleanup you’ll need later. You can also add repetitive prompts to the project rules so that they're applied every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Always Review and Validate AI Output
&lt;/h3&gt;

&lt;p&gt;Even with good prompts, AI may generate redundant, flaky, or irrelevant tests. Treat Cursor’s output as a draft: review it, remove what you don’t need, and refine. Requestly makes this easy since tests are just JSON with embedded scripts - you can see exactly what changed before committing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Requestly Mocks and Intercepts for Safer Testing
&lt;/h3&gt;

&lt;p&gt;Not every test should hit the real API. For failure cases, like 500 errors or rate limits, use Requestly’s &lt;a href="https://docs.requestly.com/general/api-mocking/api-mocking" rel="noopener noreferrer"&gt;mocks&lt;/a&gt; and &lt;a href="https://docs.requestly.com/general/http-interceptor/overview" rel="noopener noreferrer"&gt;intercepts&lt;/a&gt; to simulate responses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep Secrets Local
&lt;/h3&gt;

&lt;p&gt;Never paste API tokens, passwords, or other secrets into Cursor prompts. Store them in Requestly environments and variables instead. This way, you can safely run AI-generated tests without exposing credentials. Use development credentials in the development environment and put the Requestly environments folder in gitignore to prevent accidentally committing them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Version Control Your Tests
&lt;/h3&gt;

&lt;p&gt;Because Requestly stores collections and tests as JSON files, you can commit them directly to Git. This means your team can review, track history, and collaborate on test changes just as they do with application code. Each AI-generated test case can be peer-reviewed before merging — ensuring quality and accountability as your test suite scales.&lt;/p&gt;

&lt;p&gt;With this setup, teams can save a huge chunk of testing time, especially when managing multi-endpoint APIs with frequent updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Iterate Instead of One-Shot Generation
&lt;/h3&gt;

&lt;p&gt;Don’t expect perfect tests on the first try. Use Cursor iteratively: generate, review, adjust prompts, regenerate. Over time, you’ll build a solid suite of reusable tests tuned to your API’s quirks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combine AI Speed with Human Insight
&lt;/h3&gt;

&lt;p&gt;AI is great at generating lots of cases quickly, but you know your API's edge cases, failure modes, and business logic better than any model. Utilize AI for coverage and scaffolding, then supplement with human-crafted tests for the scenarios that truly matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By combining Cursor and Requestly, you can cut down on repetitive test writing while improving API test coverage. Cursor helps you quickly generate comprehensive test cases, while Requestly ensures those tests run securely in a local-first environment.&lt;/p&gt;

&lt;p&gt;The result: faster iteration, stronger reliability, and safer execution. If you’re looking to modernize your API testing workflow, give this combination &lt;a href="https://requestly.com/downloads/" rel="noopener noreferrer"&gt;a try&lt;/a&gt; - you’ll spend less time writing boilerplate tests and more time building what matters.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>api</category>
      <category>apitesting</category>
    </item>
    <item>
      <title>How to ace API Testing with Requestly</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Tue, 23 Sep 2025 06:45:59 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/how-to-ace-api-testing-with-requestly-hg3</link>
      <guid>https://dev.to/heraldofsolace/how-to-ace-api-testing-with-requestly-hg3</guid>
      <description>&lt;p&gt;Imagine this scenario: You’re debugging an API call. The endpoint only works with production tokens, but your API testing tool insists on syncing everything to the cloud. Suddenly, your production credentials are sitting on someone else’s servers, risking potential exposure.&lt;/p&gt;

&lt;p&gt;Most tools default to cloud-first, which can be limiting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your secrets live in the cloud.&lt;/li&gt;
&lt;li&gt;Most features require an online account.&lt;/li&gt;
&lt;li&gt;Tests aren’t versioned with your code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Requestly, you get a local-first experience. Your API tests run on your machine, alongside your code, version-controlled just like the rest of your workflow. Your secrets and credentials stay on your device, safe and secure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local-first API testing with Requestly
&lt;/h2&gt;

&lt;p&gt;With cloud-first API testing tools, your sensitive information lives in the cloud. One misconfigured account, one permission too broad, and you're risking &lt;a href="https://treblle.com/blog/apis-exposed-postman-data-breach-lessons" rel="noopener noreferrer"&gt;exposing your sensitive information&lt;/a&gt; to the world. Requestly emphasizes local-first API testing that lets developers test APIs without depending on remote setups. You run everything right on your own machine, so you can intercept requests, mock responses, or reproduce tricky edge cases in seconds, all while keeping tokens and sensitive data safe. Because it’s all local, you get fast, reliable feedback that makes debugging smoother and development less painful.&lt;/p&gt;

&lt;p&gt;This local-first approach means your tests can be stored next to your code, under Git. By keeping your Requestly assets under Git, you gain the benefits of collaborative development: teammates can review changes through pull requests, track history with commits, and roll back to previous versions if needed. This also fits naturally into existing workflows - your API test rules evolve alongside the code they validate.&lt;/p&gt;

&lt;p&gt;In the following few sections of this article, you'll see Requestly in action. As an example of a practical API to test, we'll be using the &lt;a href="https://docs.github.com/en/rest" rel="noopener noreferrer"&gt;GitHub REST API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Testing with Requestly
&lt;/h2&gt;

&lt;p&gt;To follow along with this tutorial, you'll need the following dependencies on your system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Latest version of &lt;a href="https://nodejs.org/en" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; and npm.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token" rel="noopener noreferrer"&gt;GitHub Personal Access Token (PAT)&lt;/a&gt;. Choose a repository where you have write access and give it read-write access for issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting up Requestly
&lt;/h3&gt;

&lt;p&gt;To make things easy, I have already created a &lt;a href="https://github.com/heraldofsolace/requestly-demo" rel="noopener noreferrer"&gt;repo&lt;/a&gt; that contains the basic setup needed for this article. Clone the repo to your machine.&lt;/p&gt;

&lt;p&gt;Install the dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can either install Requestly as a browser extension or as a desktop app. You can read about the feature differences between these two versions &lt;a href="https://docs.requestly.com/general/others/how-is-bowser-extension-different-from-a-desktop-app" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For this tutorial, I'll use the desktop app, which you can download from &lt;a href="https://requestly.com/downloads/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. So go ahead and install it.&lt;/p&gt;

&lt;p&gt;Requestly works on the concept of &lt;a href="https://docs.requestly.com/general/team/workspaces" rel="noopener noreferrer"&gt;&lt;strong&gt;Workspaces&lt;/strong&gt;&lt;/a&gt;. The demo app already has a local workspace stored, which you need to load. Click on the &lt;strong&gt;Workspaces&lt;/strong&gt; dropdown in the top-left corner and select &lt;strong&gt;+ Add&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Choose the directory where you just cloned the app, and use &lt;code&gt;requestly-demo&lt;/code&gt; as the name of the workspace. This will load the workspace of the same name that is already in the directory.&lt;/p&gt;

&lt;p&gt;Once the workspace is ready, navigate to the &lt;strong&gt;APIs&lt;/strong&gt; tab, where you'll find the &lt;strong&gt;GitHub API&lt;/strong&gt; collection. I have already prepared the requests so that we can focus on writing tests in this article. However, if you want to learn more about how to make requests, you can visit the &lt;a href="https://docs.requestly.com/general/api-client/send-api-request" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkc3l39up3181vtafecbv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkc3l39up3181vtafecbv.png" alt="List of prepared requests" width="728" height="740"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Apart from REST APIs, Requestly also supports making &lt;a href="https://docs.requestly.com/general/api-client/graphql-request" rel="noopener noreferrer"&gt;GraphQL requests&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is also an &lt;a href="https://docs.requestly.com/general/api-client/environments-and-variables/environment-variables" rel="noopener noreferrer"&gt;Environment&lt;/a&gt; named "Dev" where you'll need to put your credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;token&lt;/code&gt;: Your PAT&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;owner&lt;/code&gt;: Your GitHub username&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;repo&lt;/code&gt;: The name of the repo that you chose&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll start with simple tests, then advance to schema validation and testing workflows. We'll see how to debug and handle errors, and finally, we'll mock a third-party API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing Our First Test
&lt;/h3&gt;

&lt;p&gt;If authentication breaks, your entire API is toast! So, we'll start by testing the &lt;strong&gt;Login&lt;/strong&gt; endpoint. This will show you an example of how to handle authentication. If you click on the collection name and navigate to the &lt;strong&gt;Authorization&lt;/strong&gt; tab, you'll see that the authorization type has been set to &lt;strong&gt;Bearer Token&lt;/strong&gt;. For the token value, we're using &lt;code&gt;{{token}}&lt;/code&gt;, which retrieves the value from the environment variable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9fokeyynz4d2tf4d7xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9fokeyynz4d2tf4d7xg.png" alt="Authorization setup" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every endpoint in this collection inherits this authentication method, which means they'll automatically pass the token when you make the request.&lt;/p&gt;

&lt;p&gt;Open the &lt;strong&gt;Login&lt;/strong&gt; request and navigate to the &lt;strong&gt;Scripts&lt;/strong&gt; tab. Requestly allows you to run &lt;a href="https://docs.requestly.com/general/api-client/scripts" rel="noopener noreferrer"&gt;scripts&lt;/a&gt; before the request is sent (Pre-request scripts), or after the response is received (Post-response scripts). For the purpose of writing API tests, we'll use the Post-response scripts.&lt;/p&gt;

&lt;p&gt;Requestly uses &lt;a href="https://www.chaijs.com/api/bdd/" rel="noopener noreferrer"&gt;Chai.js&lt;/a&gt; for assertions. It exposes the &lt;code&gt;expect&lt;/code&gt; method of Chai through &lt;code&gt;rq.expect()&lt;/code&gt;, which you can use to write assertions. Requestly also lets you access the request and response objects through &lt;code&gt;rq.request&lt;/code&gt; and &lt;code&gt;rq.response&lt;/code&gt; respectively, which can be used in the tests.&lt;/p&gt;

&lt;p&gt;Our first test will make sure that the server returns &lt;code&gt;200 Ok&lt;/code&gt; response. Let's start by creating a test. We want to check that &lt;code&gt;rq.response.status&lt;/code&gt; is equal to &lt;code&gt;OK&lt;/code&gt;. So, our test will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Login is successful&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you click on &lt;strong&gt;Send&lt;/strong&gt;, Requestly will make the request and run the tests. You can see the result of the test in the &lt;strong&gt;Tests&lt;/strong&gt; tab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9yfbws79hokthn5dtxv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9yfbws79hokthn5dtxv.png" alt="The tests tab" width="663" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Requestly also allows you to check the status code using its name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can find all the status codes in the &lt;a href="https://docs.requestly.com/general/api-client/tests" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Authentication
&lt;/h3&gt;

&lt;p&gt;Now we know that the login request is successful, but how do we know that the login itself works correctly? The login endpoint also returns the logged-in user's data, which we can validate against the ground truth.&lt;/p&gt;

&lt;p&gt;Here we can check that the &lt;code&gt;login&lt;/code&gt; property matches our username that we put into the &lt;code&gt;owner&lt;/code&gt; environment variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Login returns valid data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;a&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;owner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send the requests, and if everything goes correctly, you should see the tests pass.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0lfz0lx2ar7idhenf3r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw0lfz0lx2ar7idhenf3r.png" alt="The tests result tab" width="658" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Schema Validation
&lt;/h3&gt;

&lt;p&gt;APIs act as contracts between services, and if the structure (fields, types, nesting, required attributes) changes, consumers may break, even if the data looks valid. This is why it's vital to test that the response object has the correct schema.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;List repos&lt;/strong&gt; request, we're making a request to the &lt;code&gt;/user/repos&lt;/code&gt; endpoint, which fetches all the repos of the logged-in user. Let's assume that we are integrating this API with a frontend that expects the body to be an array of repos, and each repo must have &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;private&lt;/code&gt;, and &lt;code&gt;owner&lt;/code&gt; fields. The &lt;code&gt;owner&lt;/code&gt; field must have &lt;code&gt;login&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt; fields, and the &lt;code&gt;name&lt;/code&gt; field must match the regex: &lt;code&gt;/^\.?[a-z0-9-_\.]+$/i&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Requestly, you can use helpers from Chai to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetched data has correct body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check that body is an array&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;an&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;that&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;empty&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check required fields&lt;/span&gt;
    &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;owner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;include&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c1"&gt;// Match regex&lt;/span&gt;
    &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\.?[&lt;/span&gt;&lt;span class="sr"&gt;a-z0-9-_&lt;/span&gt;&lt;span class="se"&gt;\.]&lt;/span&gt;&lt;span class="sr"&gt;+$/i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test works, but it's very verbose. If you have a more complicated JSON schema, you'll have to spend a lot of time writing out each property one by one. This is also unmaintainable. If the schema changes, changing it in the test will feel like finding a needle in a haystack. You can use &lt;code&gt;.to.have.jsonSchema&lt;/code&gt; instead, which lets you pass a JSON schema.&lt;/p&gt;

&lt;p&gt;For example, you can write the above test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Match JSON schema&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonSchema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;owner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.?[a-zA-Z0-9-_&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.]+$&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;private&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;login&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;login&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is much easier to write, understand, and maintain. Of course, let's make sure the tests work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc121zzn5t3s7uz48caow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc121zzn5t3s7uz48caow.png" alt="The tests run successfully" width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Complete Workflows
&lt;/h3&gt;

&lt;p&gt;Often, your API calls are not as simple as calling one single endpoint. You might need to call one endpoint, process the response, and use the data from the response to call another endpoint. With Requestly, you can use Environment Variables to store arbitrary data and use them in subsequent requests. We have already used one environment variable in the &lt;strong&gt;Login&lt;/strong&gt; endpoint. Let's now take a look at a more complete "workflow" example.&lt;/p&gt;

&lt;p&gt;Let's assume that we want to test the pipeline of creating, fetching, and deleting issues. We want to create an issue, fetch the same issue, and then delete it.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Create issue&lt;/strong&gt; endpoint, we're making a request to &lt;code&gt;{{base_url}}/repos/{{owner}}/{{repo}}/issues&lt;/code&gt; endpoint. The &lt;code&gt;{{...}}&lt;/code&gt; denotes an environment variable that Requestly will substitute. We're creating an issue with the following body:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Test issue from Requestly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This is a test issue created via API"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's write a test to ensure the issue is created successfully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Issue was created successfully&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;created&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test issue from Requestly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;issue_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we're checking that the title of the issue matches what we sent in the body. The last line is where we set the &lt;code&gt;issue_id&lt;/code&gt; environment variable, which we'll use in our subsequent request.&lt;/p&gt;

&lt;p&gt;Open the &lt;strong&gt;Fetch issue&lt;/strong&gt; request. You'll notice we're using the &lt;code&gt;issue_id&lt;/code&gt; in the request URL: &lt;code&gt;{{base_url}}/repos/{{owner}}/{{repo}}/issues/{{issue_id}}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's write a test to verify the correct issue is fetched. We'll verify that it has &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; fields, and matches what we created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Issues has correct schema&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonSchema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetches correct issue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Test issue from Requestly&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;body&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is a test issue created via API&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's close the issue to complete the workflow. In the &lt;strong&gt;Close issue&lt;/strong&gt; request, you'll see we're again using the &lt;code&gt;issue_no&lt;/code&gt; variable. This time, let's make sure &lt;code&gt;state&lt;/code&gt; is &lt;code&gt;closed&lt;/code&gt; in the response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Issue is closed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;jsonBody&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;state&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;closed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use this technique of chaining requests in many real-life scenarios. For example, if your API uses username and password to log in and returns a JWT, you can store it in an environment variable and use it in subsequent tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error Handling and Debugging
&lt;/h3&gt;

&lt;p&gt;So far, we've been dealing with tests that work correctly. But in practice, tests may fail. And it's not always easy to debug. Suppose the login test is suddenly failing in CI. Is it because someone pushed buggy code, or is it due to something in the test itself, such as an expired token? Requestly offers debugging features to help you understand why your tests are failing.&lt;/p&gt;

&lt;p&gt;When a test fails, Requestly prints an error message next to the test. You can try this out by sending the &lt;strong&gt;Invalid token&lt;/strong&gt; request. This request attempts to log in with an invalid token, but expects the result to be 'ok'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yjmhqq9p28mcqf3p0qz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yjmhqq9p28mcqf3p0qz.png" alt="Test fails" width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use &lt;code&gt;console.log&lt;/code&gt; to log values. You can use &lt;code&gt;rq.request&lt;/code&gt; and &lt;code&gt;rq.response&lt;/code&gt; objects to access the request and response values to help you debug the actual cause of a failing test. For example, let's log the request headers and response status code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reuest is successful&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you press &lt;code&gt;Ctrl+Alt+I&lt;/code&gt;, the Devtools console will open. If you send the request, the logs will show up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhqzb3kekmxc25xbpuad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhqzb3kekmxc25xbpuad.png" alt="The developer console" width="528" height="66"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These logs can help you debug your test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mocking &amp;amp; Intercepting for API Testing
&lt;/h2&gt;

&lt;p&gt;We’ve seen how to handle real errors like bad tokens or missing endpoints. But what about failures that don’t happen on their own? For example, a flaky third-party API or an edge case response you can’t easily trigger in production. That’s where mocking comes in.&lt;/p&gt;

&lt;p&gt;Real apps often depend on third-party APIs, such as payment processors, weather data, and analytics. Testing these integrations is tricky because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can’t control when the third-party API fails.&lt;/li&gt;
&lt;li&gt;You can’t control the data it sends back.&lt;/li&gt;
&lt;li&gt;Sometimes, every test call costs real money.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where &lt;a href="https://docs.requestly.com/general/api-mocking/api-mocking" rel="noopener noreferrer"&gt;API mocking&lt;/a&gt; helps. With Requestly, you can intercept an outgoing request and return a fake response. That way you can simulate failures, edge cases, or alternate data without touching the real API.&lt;/p&gt;

&lt;p&gt;Our demo repo has a &lt;code&gt;/httpbin&lt;/code&gt; endpoint that calls &lt;code&gt;http://httpbin.org/get&lt;/code&gt;. Normally, it just passes through the response. But what if HttpBin goes down? We want to see if our app handles errors gracefully.&lt;/p&gt;

&lt;p&gt;To mock an API call, you need to use Requestly's &lt;a href="https://docs.requestly.com/general/http-interceptor/overview" rel="noopener noreferrer"&gt;HTTP interceptor&lt;/a&gt;, which turns Requestly into a proxy server to intercept HTTP calls from your browsers (Chrome, Firefox, etc.), terminal app, or even the whole system. Then you can use &lt;a href="https://docs.requestly.com/general/http-rules/overview" rel="noopener noreferrer"&gt;HTTP rules&lt;/a&gt; to modify the request, response, headers, introduce delays, redirect, and much more.&lt;/p&gt;

&lt;p&gt;As of writing this article, this feature is not available in a local workspace. So, you'll need either a team workspace or switch to the private workspace. In any case, you'll need an account. Click on the icon in the top-right corner, and sign up or sign in.&lt;/p&gt;

&lt;p&gt;Now in the workspace dropdown menu, you'll see that the &lt;strong&gt;Private workspace&lt;/strong&gt; option is enabled. Choose this option to switch to the private workspace.&lt;/p&gt;

&lt;p&gt;Go to the &lt;strong&gt;Network tab&lt;/strong&gt;, click on &lt;strong&gt;Connect apps&lt;/strong&gt;, switch to the &lt;strong&gt;Terminal processes&lt;/strong&gt; tab, click on &lt;strong&gt;Setup instructions&lt;/strong&gt;, and copy the command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5akwale22cv553neqatr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5akwale22cv553neqatr.png" alt="The terminal proxy setup instructions" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paste the command in the terminal and run it. On successful execution, you should see the message &lt;strong&gt;Requestly interception enabled&lt;/strong&gt;. Now start the server with &lt;code&gt;npm start&lt;/code&gt;. Now, any request made from the API server will pass through Requestly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Running the command provided by Requestly sets up a few environment variables, one of them being &lt;code&gt;https_proxy&lt;/code&gt;, which sets up Requestly as a proxy server. The demo app uses Axios to make requests to HttpBin. Axios automatically picks up the &lt;code&gt;https_proxy&lt;/code&gt; environment variable, so in this case, we don't have to change any code. However, suppose you use a different library (such as node-fetch) that doesn't automatically pick up the &lt;code&gt;https_proxy&lt;/code&gt; environment variable. In that case, you'll need to modify the code to use the proxy server manually.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In another terminal, invoke the &lt;code&gt;/httpbin&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl localhost:3000/httpbin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Requestly, you should see the HttpBin request show up:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w7f1xp5eq6oi3k0mp7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w7f1xp5eq6oi3k0mp7t.png" alt="The HttpBin request" width="800" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: As of writing the article, requests made with Axios show up with the wrong URL due to a &lt;a href="https://github.com/requestly/requestly/issues/301" rel="noopener noreferrer"&gt;bug&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Right-click on it and select &lt;strong&gt;Modify response body&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This will open a window where you can define an HTTP rule that will modify the response body of the intercepted request.&lt;/p&gt;

&lt;p&gt;First, give the rule a name, for example: "HttpBin error". In the &lt;strong&gt;If request&lt;/strong&gt; field, select &lt;strong&gt;URL&lt;/strong&gt; and &lt;strong&gt;Contains&lt;/strong&gt; and write &lt;code&gt;httpbin.org&lt;/code&gt; in the input field. Select &lt;code&gt;500&lt;/code&gt; from the status codes dropdown, enter an empty body, and check &lt;strong&gt;Serve this response body without making a call to the server&lt;/strong&gt;. Then, save the rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3rvcxr937ga15twk5r0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3rvcxr937ga15twk5r0.png" alt="Modify response body details" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, anytime our API server makes a request to HttpBin, Requestly will return a 500 response without actually hitting HttpBin.&lt;/p&gt;

&lt;p&gt;Let's now write the test. Go to &lt;strong&gt;APIs&lt;/strong&gt; tab, and create a new HTTP request. Provide a name such as &lt;strong&gt;Get third party error&lt;/strong&gt;, and put &lt;code&gt;http://localhost:3000&lt;/code&gt; in the URL field, and write the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should return error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can test whether my app correctly handles failures or not without having to wait for the third-party API to fail!&lt;/p&gt;

&lt;p&gt;With these tests in place - from happy paths to deliberate failures - we've covered the core scenarios of real-world API testing. Now it's your turn to use Requestly for testing your APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Effective API testing goes beyond checking if an endpoint returns data - it ensures reliability, catches edge cases, and streamlines development. By going local-first with Requestly, you can quickly test and mock APIs without juggling cloud accounts or heavy setups. Start small with a few tests, then layer on more complexity as your needs grow. The best way to see the value is to &lt;a href="https://requestly.com/downloads/" rel="noopener noreferrer"&gt;try it in your own workflow&lt;/a&gt; and feel how much smoother debugging and iteration can become.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>apitesting</category>
    </item>
    <item>
      <title>(Almost) Everything You Need To Know About Pointers in C</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Thu, 09 Sep 2021 07:09:05 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/almost-everything-you-need-to-know-about-pointers-in-c-53na</link>
      <guid>https://dev.to/heraldofsolace/almost-everything-you-need-to-know-about-pointers-in-c-53na</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;When I was first starting out with C, pointers were something that confused me the most. I was scared of pointers and could never understand how to use them. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No, I didn't say that! In fact, pointers have always been intuitive to me. But most of the students starting to learn C are put off by the idea of pointers. It is one of those areas of C which are not explained properly to students. resulting in many misconceptions about them.&lt;/p&gt;

&lt;p&gt;In this huge post, I have compiled almost everything that is fundamental to pointers. Of course, it is a huge topic, and it's not possible to cover the entirety of it in one post, but once you know these fundamentals, you'll be able to use them more efficiently, and hopefully will be able to tackle pointers in a program.&lt;/p&gt;

&lt;p&gt;Let's start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beginning With Pointer Sorcery
&lt;/h2&gt;

&lt;p&gt;One fine afternoon, you are lying on your couch and thinking about the year 2038 problem and the end of the universe, and suddenly your friend calls you and asks "Hey, I want to come over and contemplate our existence, but I do not know where your house is!"&lt;/p&gt;

&lt;p&gt;You say, "No problem buddy. I'll give you a copy of my home."&lt;/p&gt;

&lt;p&gt;Of course, you'd never say that. Instead, you will give him your address so that he can come over. Now, you could make him a copy of your home if you're generous enough, but it takes time and defeats the purpose of your friend coming over. He wants to come to your house, not a copy.&lt;/p&gt;

&lt;p&gt;Now think in terms of programming. At the time when C was created, memory was scarce, and being efficient was not only needed but vital. For this reason, you'd have to be really careful while dealing with memory. You'd really not like to make unnecessary copies of something.&lt;/p&gt;

&lt;p&gt;Another case you can consider is of having "side effect" of a function. Consider this simple program.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
void f(int a) { a = 10; }
int main() {
    int a = 5;
    printf("%d\n", a);
    f(a);
    printf("%d\n", a);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;which just prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5
5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Even though you are calling the function &lt;code&gt;f&lt;/code&gt; with the variable &lt;code&gt;a&lt;/code&gt; as a parameter, and &lt;code&gt;f&lt;/code&gt; is changing the value of &lt;code&gt;a&lt;/code&gt;. the change doesn't show up in the original value of &lt;code&gt;a&lt;/code&gt;, because when you are calling the function &lt;code&gt;f&lt;/code&gt;, you are passing a copy of &lt;code&gt;a&lt;/code&gt;, not &lt;code&gt;a&lt;/code&gt; itself. In other terms, you are giving your friend a copy of your house.&lt;/p&gt;

&lt;p&gt;This is desired in most cases. You don't really want your functions to accidentally change any variable where it's not supposed to. But sometimes, you actually want the function to change a variable. You have already seen such a function that can change the actual parameter.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scanf("%d", &amp;amp;n);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;How does &lt;code&gt;scanf&lt;/code&gt; change the value of &lt;code&gt;n&lt;/code&gt;? The answer is through pointers.&lt;/p&gt;

&lt;p&gt;Also, take a look at this classic example of swap- &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void swap(int a, int b) {
    int t = a;
    a = b;
    b = t;
}
int main() {
    int a = 5, b =10;
    swap(a, b);
    printf("a = %d, b = %d\n", a, b);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It works, except it doesn't.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;swap&lt;/code&gt; does swap the variables, but since you are making a copy of &lt;code&gt;a&lt;/code&gt;, and &lt;code&gt;b&lt;/code&gt;, the change doesn't show up outside the function. But we want the function to be able to change the actual variables. So we need to have some kind of way to pass the actual &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; . But in C, there is no way you can pass "actual" variables. (which is not the case in C++).&lt;/p&gt;

&lt;p&gt;One way you might end up doing is to make &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; global &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a = 5, b = 10;
void swap() {
    int t = a;
    a = b;
    b = t;
}
int main() {
    swap();
    printf("a = %d, b = %d\n", a, b);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And it now works, because &lt;code&gt;swap&lt;/code&gt; now can access &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;, but having global variables is a real &lt;a href="https://wiki.c2.com/?GlobalVariablesAreBad" rel="noopener noreferrer"&gt;bad idea&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The way? Give &lt;code&gt;swap&lt;/code&gt; the addresses of &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;. If it has addresses of &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; , it can change them directly.&lt;/p&gt;

&lt;p&gt;Pointers are nothing but variables that hold the address of another variable.&lt;/p&gt;

&lt;p&gt;Now, where does this address come from? We know how &lt;a href="https://computer.howstuffworks.com/bytes.htm" rel="noopener noreferrer"&gt;bits and bytes work&lt;/a&gt;. The RAM of the computer can be thought of as a mess, a really long one, with lots of rooms one after another, and each byte is a room. How does the computer know which room to put data in? It gives a number to each room, and that number is the address.&lt;/p&gt;

&lt;p&gt;When I write&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;char a;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I tell the compiler "Buddy, reserve one room in the mess, and call it &lt;code&gt;a&lt;/code&gt;" . Why one room? Because the size of &lt;code&gt;char&lt;/code&gt; is 1 byte. (Note that C's definition of a byte is basically the &lt;code&gt;sizeof char&lt;/code&gt; , which in some rare cases might not be actually 1 byte in the machine, however, it is always 1 byte in C)&lt;/p&gt;

&lt;p&gt;If I write&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int b;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I tell the compiler to reserve the number of rooms necessary for &lt;code&gt;int&lt;/code&gt; and call it &lt;code&gt;b&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side rant: People coming from Turbo C, and being told size of &lt;code&gt;int&lt;/code&gt; is 2 bytes, it's not necessarily so, and probably not so in any modern computer. The C standard guarantees at least 2 bytes for &lt;code&gt;int&lt;/code&gt; and on my machine &lt;code&gt;sizeof(int)&lt;/code&gt; is 4, so we will stick to that for the rest of this post.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now that our &lt;code&gt;b&lt;/code&gt; has 4 rooms, it will stay in the rooms starting from the first one. So that when we say "address of b", we actually mean "address of the starting or ending byte of b". (See &lt;a href="https://chortle.ccsu.edu/AssemblyTutorial/Chapter-15/ass15_3.html" rel="noopener noreferrer"&gt;big endian and little endian&lt;/a&gt;. For this tutorial, let's assume it's the ending byte because it is so on my machine)&lt;/p&gt;

&lt;p&gt;In order to get the address of &lt;code&gt;b&lt;/code&gt; and store it, we need to use a pointer variable. Just like any other variable, a pointer also has a type, defined by the type of the thing it points to. The syntax is &lt;code&gt;type_of_the_thing_it_points_to *name&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;char *pa;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that the asterisk need not be adjoined to the variable name. Any of these is valid - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;char* pa;
char *pa;
char * pa;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We will prefer the 2nd syntax. We will see in a short while why.&lt;/p&gt;

&lt;p&gt;Let's first see how to assign a value to a pointer. In order to make a pointer point to a variable, we have to store the address of the variable in the pointer. The syntax for getting the address of a variable is &lt;code&gt;&amp;amp;variable_name&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;char a;
char *pa = &amp;amp;a; // pa now contains the address of a

printf("%p", pa); // %p is the format specifier to print a pointer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you run this program, you will see something like &lt;code&gt;0x7ffc2fc4ff27&lt;/code&gt;. That is the value of the pointer, which is the address of the variable &lt;code&gt;a&lt;/code&gt; (this is in hexadecimal). This value is not fixed. If you run the program again, the value will likely change, because &lt;code&gt;a&lt;/code&gt; will be stored somewhere else.&lt;/p&gt;

&lt;p&gt;One thing you might have noticed. Although we are declaring it as &lt;code&gt;*pa&lt;/code&gt;, the * is not used when printing the pointer. In fact, * is not a part of the name of the pointer. The name of the pointer is just &lt;code&gt;pa&lt;/code&gt;. The * is instead used to get the value of whatever thing the pointer is pointing to (known as dereferencing).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;char a = 'a';
char *pa = &amp;amp;a;

printf("%p\n", pa); // prints the value of pa
printf("%c", *pa); // prints the value of a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So, quickly revise - &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;pa&lt;/code&gt; is the value of the pointer, which is the address of &lt;code&gt;a&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*pa&lt;/code&gt; is the value of the thing &lt;code&gt;pa&lt;/code&gt; is pointing to, in this case &lt;code&gt;a&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One more time.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;pointer_name&lt;/code&gt; is the value of the pointer itself.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*pointer_name&lt;/code&gt; is the value of the thing the pointer points to.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, this should be clear.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;char a = 'a', b = 'b';
char * pa = &amp;amp;a; // pa points to a
*pa = 'c'; // change the value of whatever pa is pointing to, in this case a
printf("%c", a); //prints c
pa = &amp;amp;b; // change the pointer itself. pa now points to b
*pa = 'd' // change the value of whatever pa is pointing to, in this case b
printf("%c", a); //prints c, because a is unchanged as pa is no more pointing to a
printf("%c", b); //prints d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now we can rewrite the swap function as follows - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void swap(int *a, int *b) {
    int t = *a;
    *a = *b;
    *b = t;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And call it with the addresses &lt;code&gt;swap(&amp;amp;a, &amp;amp;b)&lt;/code&gt;. This works and the change shows up outside the function too. Because once you have the address of a variable, you know where it lives in memory so you can freely change it.&lt;/p&gt;

&lt;p&gt;You might have a valid question. Since all pointers are just addresses, which are basically numbers, why is the type of the thing it points to necessary? Why do we distinguish between &lt;code&gt;char*&lt;/code&gt; and &lt;code&gt;int*&lt;/code&gt; although both of them are just some numbers?&lt;/p&gt;

&lt;p&gt;The answer is clear. When you dereference a pointer, the compiler needs to know what data type is the object. Remember that address of a variable is just the address of the ending byte of the variable. In order to read the variable, the compiler needs to know its type so that it knows how many bytes to read.&lt;/p&gt;

&lt;p&gt;Consider this program&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int main() {
    int a = 1101214537;
    char *pa = &amp;amp;a;
    printf("%c", *pa);

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It prints (ignore the compiler warning)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What happened here?&lt;/p&gt;

&lt;p&gt;If you represent &lt;code&gt;1101214537&lt;/code&gt; in binary it is &lt;code&gt;01000001 10100011 00110011 01001001&lt;/code&gt; . So &lt;code&gt;&amp;amp;a&lt;/code&gt; which is the address of &lt;code&gt;a&lt;/code&gt; points to the byte in memory that contains the last byte of the number, which is &lt;code&gt;01001001&lt;/code&gt;. When I dereference &lt;code&gt;pa&lt;/code&gt;, the compiler sees that it points to &lt;code&gt;char&lt;/code&gt; so it reads only one byte at that address, giving the value &lt;code&gt;01001001&lt;/code&gt; which &lt;code&gt;73&lt;/code&gt;, the ASCII for &lt;code&gt;I&lt;/code&gt;. This is why the type is absolutely and you should not mix and match types unless you are absolutely sure of what you are doing. (We'll see a few examples)&lt;/p&gt;

&lt;p&gt;Remember we told that we will prefer &lt;code&gt;int *pa&lt;/code&gt; rather than &lt;code&gt;int* pa&lt;/code&gt; although they are the same? The reason is to safeguard against the following common misconception. Can you find the difference?&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a, b; // a and b both are int
int* pa, pb; // whoopsie! pb is not a pointer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you are a beginner, you will assume that since &lt;code&gt;int a, b&lt;/code&gt; makes both &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; as &lt;code&gt;int&lt;/code&gt; , then &lt;code&gt;int* pa, pb&lt;/code&gt; will make both &lt;code&gt;pa&lt;/code&gt; and &lt;code&gt;pb&lt;/code&gt; as &lt;code&gt;int*&lt;/code&gt;. But it doesn't. The reason is &lt;code&gt;*&lt;/code&gt; "binds" to the variable name, not the type name. If instead, you'd have written&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int *pa, pb;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;you'd rightly conclude &lt;code&gt;pa&lt;/code&gt; is a pointer to int, and &lt;code&gt;pb&lt;/code&gt; is just &lt;code&gt;int&lt;/code&gt;. Hence I prefer to write the &lt;code&gt;*&lt;/code&gt; with the variable name, however, there are compelling reasons for the other style as well, and if you are careful enough, you can use the other style as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  NULL and void pointer
&lt;/h3&gt;

&lt;p&gt;These two are a special types of pointers in C. The Null pointer is used to denote that the pointer doesn't point to a valid memory location.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int *pa = NULL;
char *pb = NULL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We use Null pointer in various ways, for example, to denote failure, or mark the end of a list of unknown size, etc. Dereferencing a Null pointer is undefined behavior and your program will likely crash.&lt;/p&gt;

&lt;p&gt;Note that the Null pointer is not the same as pointer to memory address 0, although it's very likely to be so. There are exceptions, for example in small embedded devices where address 0 might be a valid location.&lt;/p&gt;

&lt;p&gt;Void pointer is one more interesting pointer in C. Basically void pointer "throws away" the type of a pointer. It is a general-purpose pointer that can hold any type of pointer and can be cast to any type of pointer. The following are all valid - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a;
char b;
float c;
void *p = &amp;amp;a;
p = &amp;amp;b;
p = &amp;amp;c;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;But you can't dereference a &lt;code&gt;void *&lt;/code&gt; because it doesn't have a type. Trying to dereference a &lt;code&gt;void *&lt;/code&gt; will give you an error. However, you can cast it to anything you want and then dereference it, although it's not a very good idea and it violates the type aliasing rules.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a = 65;
void *p = &amp;amp;a;
char *c = (char *) p;
printf("%c\n", *c);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we're removing the type of &lt;code&gt;&amp;amp;a&lt;/code&gt; through &lt;code&gt;p&lt;/code&gt; and casting it to a &lt;code&gt;char *&lt;/code&gt;. Essentially &lt;code&gt;a&lt;/code&gt; is getting read as a &lt;code&gt;char&lt;/code&gt; and this prints &lt;code&gt;A&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Be careful during casting. You should use void pointers only if you are absolutely sure of what you're doing.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a = 65;
void *p = &amp;amp;a;
int (*f)(int) = (int (*)(int)) p; // cast as a function pointer (discussed later)
f(2); // Segmentation fault
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Sometimes you'll see &lt;code&gt;char *&lt;/code&gt; used as a generic pointer as well. This is because &lt;code&gt;void *&lt;/code&gt; was not present in old versions of C, and some practice remains, or maybe the code needs to do pointer arithmetic on that pointer.&lt;/p&gt;

&lt;p&gt;Generally &lt;code&gt;void *&lt;/code&gt;&lt;code&gt;i&lt;/code&gt;s used in places where you expect to work with pointers to multiple types. As an example, consider the famous &lt;code&gt;memcpy&lt;/code&gt; function which copies a block of memory. Here is the signature of &lt;code&gt;memcpy&lt;/code&gt; - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void * memcpy ( void * destination, const void * source, size_t num );
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you see, it accepts &lt;code&gt;void *&lt;/code&gt;, which means it works with any type of pointers. As for an example (copied from &lt;a href="https://www.cplusplus.com/reference/cstring/memcpy/" rel="noopener noreferrer"&gt;cplusplus&lt;/a&gt;) - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* memcpy example */
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

struct {
  char name[40];
  int age;
} person, person_copy;

int main ()
{
  char myname[] = "Pierre de Fermat";

  /* using memcpy to copy string: */
  memcpy ( person.name, myname, strlen(myname)+1 );
  person.age = 46;

  /* using memcpy to copy structure: */
  memcpy ( &amp;amp;person_copy, &amp;amp;person, sizeof(person) );

  printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );

  return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In line 15, we invoked &lt;code&gt;memcpy&lt;/code&gt; with &lt;code&gt;char *&lt;/code&gt; and in line 19, we invoked &lt;code&gt;memcpy&lt;/code&gt; with a pointer to structure, and they both work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointer arithmetic
&lt;/h3&gt;

&lt;p&gt;Since pointers are just like other variables, you'd expect that we should be able to do arithmetic with them. We can, but there's a catch. First of all, we are only allowed these 2 operations - &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Addition (and hence subtraction) of an integer constant to a pointer.&lt;/li&gt;
&lt;li&gt;Subtraction of two pointers of the same type.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's see them one by one&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a;
int *pa = &amp;amp;a;

printf("pa = %p\n", pa);
printf("pa + 1 = %p\n", pa + 1);
printf("pa - 1 = %p\n", pa - 1);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pa = 0x7ffdd7eeee64
pa + 1 = 0x7ffdd7eeee68
pa - 1 = 0x7ffdd7eeee60
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Strangely, it seems &lt;code&gt;pa+1&lt;/code&gt; increments the pointer by 4, and not by 1. The reason lies in the datatype of the thing it points to, in this case, &lt;code&gt;int&lt;/code&gt;. Remember that a pointer must always point to something. When you increment the pointer by 1, it points to the next thing.&lt;/p&gt;

&lt;p&gt;In this case, &lt;code&gt;pa&lt;/code&gt; points to an &lt;code&gt;int&lt;/code&gt;. Where is the next &lt;code&gt;int&lt;/code&gt;? After 4 bytes of course, because the size of int is 4 bytes.&lt;/p&gt;

&lt;p&gt;Similarly &lt;code&gt;pa-1&lt;/code&gt; points to the previous &lt;code&gt;int&lt;/code&gt; which lies 4 bytes before.&lt;/p&gt;

&lt;p&gt;By the same logic, &lt;code&gt;pa+2&lt;/code&gt; points to the &lt;code&gt;int&lt;/code&gt; 2 places after &lt;code&gt;a&lt;/code&gt; that is &lt;code&gt;4 * 2 = 8&lt;/code&gt; bytes after &lt;code&gt;a&lt;/code&gt;, and &lt;code&gt;pa+n&lt;/code&gt; points to the integer &lt;code&gt;n&lt;/code&gt; places after &lt;code&gt;a&lt;/code&gt; which is &lt;code&gt;4n&lt;/code&gt; bytes after &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;An observant reader might have noticed that things are looking almost like an array, and he/she is not wrong completely. In a few minutes, we shall explore the idea of array using pointers. Before let's talk about the subtraction of pointers.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a;
int *pa = &amp;amp;a;
int *pb = pa + 2;
printf("pa = %p\n", pa);
printf("pb = %p\n", pb);
printf("pb - pa = %ld\n", pb - pa);
printf("pa - pb = %ld\n", pa - pb);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pa = 0x7ffec09d685c
pb = 0x7ffec09d6864
pb - pa = 2
pa - pb = -2  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Similar to the previous case, although the difference between &lt;code&gt;pa&lt;/code&gt; and &lt;code&gt;pb&lt;/code&gt; is of 8 bytes as numbers, as pointers the difference is 2. The negative sign of &lt;code&gt;pa-pb&lt;/code&gt; implies that &lt;code&gt;pb&lt;/code&gt; points after &lt;code&gt;pa&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To quickly summarise - &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If I have &lt;code&gt;some_data_type *p&lt;/code&gt;, then &lt;code&gt;pa + n&lt;/code&gt; increments the pointer by &lt;code&gt;n * sizeof(some_data_type)&lt;/code&gt; bytes.&lt;/li&gt;
&lt;li&gt;If I have &lt;code&gt;some_data_type *p, *q&lt;/code&gt; then &lt;code&gt;p - q&lt;/code&gt; is equal to the difference in bytes divided by &lt;code&gt;sizeof(some_data_type)&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's consider what happens if we mix indirection and prefix or postfix increment/decrement operators. Can you guess what each of these does? I have omitted the data types so that you can't guess ;-). Assume &lt;code&gt;p&lt;/code&gt; points to &lt;code&gt;int&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = *p++;
x = ++*p;
x = *++p;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In order to answer, you have to remember the precedence - &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Postfix &lt;code&gt;++&lt;/code&gt; and &lt;code&gt;--&lt;/code&gt; have higher precedence than &lt;code&gt;*&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Prefix &lt;code&gt;++&lt;/code&gt; and &lt;code&gt;--&lt;/code&gt; have the same precedence as &lt;code&gt;*&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since the &lt;code&gt;*&lt;/code&gt; operator is itself a prefix, you'll never have a problem with prefix increment or decrement. You can tell just by the order of the operator. For the postfix operator, remember that postfix works first, then indirection.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;*p++&lt;/code&gt; is same as &lt;code&gt;*(p++)&lt;/code&gt;. So, the value of &lt;code&gt;p&lt;/code&gt; will be used in the expression, then &lt;code&gt;p&lt;/code&gt; will be incremented. So &lt;code&gt;x&lt;/code&gt; gets the value of &lt;code&gt;*p&lt;/code&gt; and &lt;code&gt;p&lt;/code&gt; becomes &lt;code&gt;p+1&lt;/code&gt;, so that the type of &lt;code&gt;x&lt;/code&gt; ought to be &lt;code&gt;int&lt;/code&gt; too.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a = 5;
int *p = &amp;amp;a;
int x;
printf("Before:\n");
printf("a = %d\n", a);
printf("p = %p\n", p);
x = *p++;
printf("After\n");
printf("a = %d\n", a);
printf("p = %p\n", p);
printf("x = %d\n", x);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:
a = 5
p = 0x7ffe82ae9eb0
After
a = 5
p = 0x7ffe82ae9eb4
x = 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;++*p&lt;/code&gt; will probably not arise confusion. This is the same as &lt;code&gt;++ (*p)&lt;/code&gt;. So, first &lt;code&gt;p&lt;/code&gt; is dereferenced, and then &lt;code&gt;++&lt;/code&gt; is applied. So whatever &lt;code&gt;p&lt;/code&gt; was pointing to gets incremented by 1 and then it is assigned to &lt;code&gt;x&lt;/code&gt;, and &lt;code&gt;p&lt;/code&gt; is unchanged. So the type of &lt;code&gt;x&lt;/code&gt; is again &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a = 5;
int *p = &amp;amp;a;
int x;
printf("Before:\n");
printf("a = %d\n", a);
printf("p = %p\n", p);
x = ++*p;
printf("After\n");
printf("a = %d\n", a);
printf("p = %p\n", p);
printf("x = %d\n", x);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:
a = 5
p = 0x7fff1484e210
After
a = 6
p = 0x7fff1484e210
x = 6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And finally &lt;code&gt;* ++p&lt;/code&gt; is same as &lt;code&gt;* (++p)&lt;/code&gt;. So, first &lt;code&gt;p&lt;/code&gt; gets incremented by 1, and then it is dereferenced. So &lt;code&gt;x&lt;/code&gt; gets the value of whatever is this incremented pointer pointing to.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a = 5;
int *p = &amp;amp;a;
int x;
printf("Before:\n");
printf("a = %d\n", a);
printf("p = %p\n", p);
x = *++p;
printf("After\n");
printf("a = %d\n", a);
printf("p = %p\n", p);
printf("x = %d\n", x);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:
a = 5
p = 0x7ffd4bad9c90
After
a = 5
p = 0x7ffd4bad9c94
x = 32765
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can also compare pointers using relational operators like &lt;code&gt;==&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt; etc. but there's a catch. You can only compare two pointers using &lt;code&gt;&amp;lt;=, &amp;lt;, &amp;gt;=, &amp;gt;&lt;/code&gt; if they both are pointers of the same type, and of the same array or same aggregate object. Otherwise, it is undefined behavior.&lt;/p&gt;

&lt;p&gt;Quoting C11 - &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When two pointers are compared, the result depends on the relative locations in the address space of the objects pointed to. If two pointers to object types both point to the same object, or both point one past the last element of the same array object, they compare equal. If the objects pointed to are members of the same aggregate object, pointers to structure members declared later compare greater than pointers to members declared earlier in the structure, and pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. All pointers to members of the same union object compare equal. If the expression &lt;code&gt;P&lt;/code&gt; points to an element of an array object and the expression &lt;code&gt;Q&lt;/code&gt; points to the last element of the same array object, the pointer expression &lt;code&gt;Q+1&lt;/code&gt; compares greater than &lt;code&gt;P&lt;/code&gt;. *&lt;strong&gt;&lt;em&gt;In all other cases, the behavior is undefined.&lt;/em&gt;&lt;/strong&gt;*&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Take a look - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;typedef struct some_struct {
    int p;
    int q;
} some_struct;

some_struct a = {1, 2}; 
int *p = &amp;amp;a.p;
int *q = &amp;amp;a.q;
if(p &amp;gt; q) puts("Hi\n");
else puts("Bye\n");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints &lt;code&gt;Bye&lt;/code&gt;. Well first of all this comparison is valid since &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; are both pointers to &lt;code&gt;int&lt;/code&gt; and also they both point to elements of the same &lt;code&gt;struct&lt;/code&gt;. Since &lt;code&gt;q&lt;/code&gt; was declared later in &lt;code&gt;some_struct&lt;/code&gt; , &lt;code&gt;q&lt;/code&gt; compares greater to &lt;code&gt;p&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For equality, the restriction is a bit slack. You can compare any two pointers as long as they have the same type, or one of them is a null pointer or void pointer. And they compare equal if they point to the same object, or if both are null (doesn't matter if types don't match), or if both are pointing to members of the same union.&lt;/p&gt;

&lt;p&gt;Let's demonstrate the last point.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;typedef union some_union {
    int p;
    int q;
} some_union;

some_union a;
int *p = &amp;amp;a.p;
int *q = &amp;amp;a.q;

if(p == q) {
    puts("Equal\n");
} else {
    puts("Not equal\n");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints &lt;code&gt;Equal&lt;/code&gt; although &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt; point to different things, they are within the same union.&lt;/p&gt;

&lt;p&gt;Since pointers are just numbers, can you put any integer in them? The answer is yes, but be careful of what you put. In fact, be careful when you dereference it. If you try to dereference an invalid address, your program will likely segfault and crash.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int *x = (int *) 1;
printf("%d\n", *x);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This instantly segfaults.&lt;/p&gt;

&lt;h2&gt;
  
  
  Admitted to Hogwarts School Of Pointer Magic
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pointers and Arrays
&lt;/h3&gt;

&lt;p&gt;Let's now move to some advanced sorcery - array and pointers.&lt;/p&gt;

&lt;p&gt;We know that an array stores its elements contiguously in memory. Which means the elements are stored in order one after another. So if we have &lt;code&gt;int arr[10]&lt;/code&gt;, we know &lt;code&gt;arr[1]&lt;/code&gt; lies right after &lt;code&gt;arr[0]&lt;/code&gt;, &lt;code&gt;arr[2]&lt;/code&gt; lies right after &lt;code&gt;arr[1]&lt;/code&gt; and so on. So if I have a pointer to &lt;code&gt;arr[0]&lt;/code&gt; and I increment it by 1, it should point to &lt;code&gt;arr[1]&lt;/code&gt;. If I increment it by 1 again, it should point to &lt;code&gt;arr[2]&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;In fact, there are so many similarities between arrays and pointers, that we can talk about the equivalence of arrays and pointers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Word of caution! This does not mean arrays and pointers are the same and you can use one in place of another. This misconception is quite common and ends up being harmful. Arrays and pointers are very different things, but pointer arithmetic and array indexing are equivalent.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For starters, the name of an array "decays" into a pointer to the first element. What do I mean by that? Consider this code - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int arr[10];
int *pa = &amp;amp;(arr[0]); // pointer to the first element
int *pb = arr; // What

printf("pa = %p\n", pa);
printf("pb = %p\n", pb);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pa = 0x7ffef7706bb0
pb = 0x7ffef7706bb0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;But aren't we mixing up datatypes in the case of &lt;code&gt;pb&lt;/code&gt;? &lt;code&gt;pb&lt;/code&gt; is a pointer to &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;arr&lt;/code&gt; is an array of int!&lt;/p&gt;

&lt;p&gt;Turns out that &lt;code&gt;arr&lt;/code&gt; is &lt;em&gt;converted to&lt;/em&gt; a pointer to the first element. So that &lt;code&gt;arr&lt;/code&gt; and &lt;code&gt;&amp;amp;(arr[0])&lt;/code&gt; is equivalent.&lt;/p&gt;

&lt;p&gt;Quick note: indexing operator &lt;code&gt;[]&lt;/code&gt; has higher precendence than &lt;code&gt;*&lt;/code&gt; so that &lt;code&gt;* arr[0]&lt;/code&gt; is same as &lt;code&gt;* (arr[0])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Let's do even more funny stuff - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int arr[3] = {1, 2, 3};
int *pa = arr;


printf("arr[1] = %d\n", arr[1]); // 2nd element using array indexing
printf("*(pa + 1) = %d\n", *(pa + 1)); // 2nd element using pointer arithmetic
printf("pa[1] = %d\n", pa[1]); // What
printf("*(arr + 1) = %d\n", *(arr + 1)); // Whatt
printf("1[arr] = %d\n", 1[arr]); // Whattt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The first &lt;code&gt;printf&lt;/code&gt; is ok. &lt;code&gt;arr[1]&lt;/code&gt; means the 2nd element of &lt;code&gt;arr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We just reasoned about the 2nd line. &lt;code&gt;pa&lt;/code&gt; points to the first element of &lt;code&gt;arr&lt;/code&gt;. So &lt;code&gt;pa+1&lt;/code&gt; will point to the next &lt;code&gt;int&lt;/code&gt; in memory, which is &lt;code&gt;arr[1]&lt;/code&gt; because array elements are stored contiguously.&lt;/p&gt;

&lt;p&gt;But in the 3rd and 4th lines, aren't we mixing up array and pointer syntax? Well, turns out that &lt;code&gt;arr[i]&lt;/code&gt; is just the same as &lt;code&gt;*(arr + i)&lt;/code&gt; and this is (almost) what happens internally when you write &lt;code&gt;arr[i]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Similarly &lt;code&gt;*(pa + i)&lt;/code&gt; is the same as &lt;code&gt;pa[i]&lt;/code&gt;. Pointer arithmetic works both on arrays and pointers. Similarly, array indexing works on both pointers and arrays.&lt;/p&gt;

&lt;p&gt;And for the last part, &lt;code&gt;arr[1]&lt;/code&gt; is the same as &lt;code&gt;*(arr + 1)&lt;/code&gt; which is the same as &lt;code&gt;*(1 + arr)&lt;/code&gt; which should be the same as &lt;code&gt;1[arr]&lt;/code&gt;. This is one of those weird quirks of C.&lt;/p&gt;

&lt;p&gt;Does this mean you can mix and match pointers and arrays? The answer is a big fat no. The reason is although &lt;code&gt;arr[i]&lt;/code&gt; and &lt;code&gt;pa[i]&lt;/code&gt; give you the same result, i. e. the 2nd element of &lt;code&gt;arr&lt;/code&gt;, the way they reach there is quite different.&lt;/p&gt;

&lt;p&gt;Consider the code&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include&amp;lt;stdio.h&amp;gt;
int main() {
    int arr[3] = {1, 2, 3};
    int *pa = arr;
    int a = arr[1];
    int b = pa[1];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's look at the assembly code generated by the compiler. I used &lt;a href="//__GHOST_URL__/p/7a2aaf27-dbd2-4822-8dd4-3bbb6da203ac/godbolt.org"&gt;Compiler Explorer&lt;/a&gt;. Don't worry if you can't read assembly. We'll go together.&lt;/p&gt;

&lt;p&gt;We are interested in lines 5 and 6. Here's the related assembly for &lt;code&gt;int arr[3] = {1, 2, 3}&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mov     DWORD PTR [rbp-28], 1
mov     DWORD PTR [rbp-24], 2
mov     DWORD PTR [rbp-20], 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In case you are seeing assembly for the first time, &lt;code&gt;rbp&lt;/code&gt; is the base pointer register that holds the memory address of the base of the current stack frame. Don't worry about what that means. For now think of &lt;code&gt;rbp&lt;/code&gt; as a pointer variable, which points to some location in memory.&lt;/p&gt;

&lt;p&gt;Here the contents of &lt;code&gt;arr&lt;/code&gt; is being put in memory. For example, consider the first line. The &lt;code&gt;mov&lt;/code&gt; instruction puts the value 1 somewhere in memory. The &lt;code&gt;DWORD PTR&lt;/code&gt; tells that it is of size 32 bit or 4 bytes as it is an &lt;code&gt;int&lt;/code&gt;. The syntax &lt;code&gt;[rbp - 28]&lt;/code&gt; means the content of the memory location at the address &lt;code&gt;rbp-28&lt;/code&gt;. Remember that &lt;code&gt;rbp&lt;/code&gt; is like a pointer. So it is the same as doing &lt;code&gt;* (rbp - 28)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Putting everything together, we see that the first line puts the value 1 in the memory address pointed by &lt;code&gt;rbp-28&lt;/code&gt;. The next value should be stored right after it, i. e. after 4 bytes. Which should be pointed by &lt;code&gt;rbp-24&lt;/code&gt; and indeed that is where 2 is stored. And finally, 3 is stored in the memory address pointed by &lt;code&gt;rbp-20&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, we see that the address of the first element is &lt;code&gt;rbp-28&lt;/code&gt;. So we'd expect this should be reflected in the line &lt;code&gt;int *pa = arr;&lt;/code&gt;. And indeed it is - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lea     rax, [rbp-28]
mov     QWORD PTR [rbp-8], rax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;lea&lt;/code&gt; means &lt;code&gt;load effective address&lt;/code&gt; which calculates &lt;code&gt;rbp-28&lt;/code&gt; and stores the &lt;strong&gt;address&lt;/strong&gt; in &lt;code&gt;rax&lt;/code&gt; rather than fetching the content of the memory address  &lt;code&gt;rbp-28&lt;/code&gt; and storing the &lt;em&gt;content&lt;/em&gt;. In other words, it just copies the address of the first element in &lt;code&gt;rax&lt;/code&gt; register and then in the memory location &lt;code&gt;rbp-8&lt;/code&gt; which is our &lt;code&gt;pa&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now let's look at &lt;code&gt;int a = arr[1]&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mov     eax, DWORD PTR [rbp-24]
mov     DWORD PTR [rbp-12], eax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So here first the content of &lt;code&gt;rbp-24&lt;/code&gt; is loaded into &lt;code&gt;eax&lt;/code&gt; and then stored in &lt;code&gt;rbp-12&lt;/code&gt; which is our &lt;code&gt;a&lt;/code&gt;. The interesting thing to notice is that the compiler knows the first element of &lt;code&gt;arr&lt;/code&gt; is at &lt;code&gt;rbp-28&lt;/code&gt; so when you write &lt;code&gt;arr[1]&lt;/code&gt; it directly offsets the base address by &lt;code&gt;and gets&lt;/code&gt;rbp-24`. This happens in compile time.&lt;/p&gt;

&lt;p&gt;Now let's look at &lt;code&gt;int b = pa[1];&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mov     rax, QWORD PTR [rbp-8]
mov     eax, DWORD PTR [rax+4]
mov     DWORD PTR [rbp-16], eax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we see first the value stored at &lt;code&gt;rbp-8&lt;/code&gt; is moved to &lt;code&gt;rax&lt;/code&gt;. Remember this was our &lt;code&gt;pa&lt;/code&gt; variable? So first the value stored at &lt;code&gt;pa&lt;/code&gt; is read. Then it is offset by 1, so we get &lt;code&gt;rax + 4&lt;/code&gt; and we read the value at &lt;code&gt;rax+4&lt;/code&gt; and store it to &lt;code&gt;eax&lt;/code&gt;. Finally, we store the value from &lt;code&gt;eax&lt;/code&gt; to &lt;code&gt;rbp-16&lt;/code&gt; which is the &lt;code&gt;b&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;The noticeable difference is that it takes one extra instruction in case of pointer. Because array address is fixed, when you write &lt;code&gt;arr&lt;/code&gt;, the compiler knows what you're talking about. But a pointer value can be changed. So when you write &lt;code&gt;pa&lt;/code&gt;, the value of &lt;code&gt;pa&lt;/code&gt; needs to be read first and then it can be used.&lt;/p&gt;

&lt;p&gt;Now suppose something like this. You have two files. One contains a global array like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int arr[3] = { 1, 2, 3 };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And in another file, you get carried away by the equivalence of array and pointer and write&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;extern int *arr;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In other words, you have declared &lt;code&gt;arr&lt;/code&gt; as a pointer but defined as an array. What will happen if you write &lt;code&gt;int a = arr[1]&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;The answer is - something catastrophic. Let's see why.&lt;/p&gt;

&lt;p&gt;Let's assume the array elements are stored just like before - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mov     DWORD PTR [rbp-28], 1
mov     DWORD PTR [rbp-24], 2
mov     DWORD PTR [rbp-20], 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;But in our second file, we are doing &lt;code&gt;arr[1]&lt;/code&gt;. So it will do something like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mov     rax, QWORD PTR [rbp-28]
mov     eax, DWORD PTR [rax+4]
mov     DWORD PTR [rbp-16], eax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Can you see the problem? We are reading the content at &lt;code&gt;rbp-28&lt;/code&gt;, but the content is 1, the first element of the array. So, essentially we are reading the content of memory address &lt;code&gt;1+4=5&lt;/code&gt; which is an invalid location!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bottom line: Don't mix and match.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another difference is that a pointer name is a variable, but an array name is not. So you can do &lt;code&gt;pa++&lt;/code&gt; and &lt;code&gt;pa=arr&lt;/code&gt; but you cannot do &lt;code&gt;arr=pa&lt;/code&gt; and &lt;code&gt;arr++&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;But, there is a case where arrays and pointers are the same. That is in function parameters - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void f(int *pa, int arr[]) {
    int a = pa[1];
    int b = arr[1];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What is the difference between &lt;code&gt;arr&lt;/code&gt; and &lt;code&gt;pa&lt;/code&gt;? There is no difference&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a = pa[1]
mov     rax, QWORD PTR [rbp-24]
mov     eax, DWORD PTR [rax+4]
mov     DWORD PTR [rbp-4], eax

int b = arr[1]
mov     rax, QWORD PTR [rbp-32]
mov     eax, DWORD PTR [rax+4]
mov     DWORD PTR [rbp-8], eax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The compiler treats &lt;code&gt;arr&lt;/code&gt; and &lt;code&gt;pa&lt;/code&gt; both as pointers, and that's about the only case you can be certain that using pointer in place of array works.&lt;/p&gt;

&lt;p&gt;Technically, this is an illustration of an array-like syntax being used to declare pointers, rather than an example of pointers and arrays being the same.&lt;/p&gt;

&lt;p&gt;Since pointers are like any other variable, you can have a pointer to pointers too.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int **pa;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;pa&lt;/code&gt; is a pointer to pointer to &lt;code&gt;int&lt;/code&gt;. So, &lt;code&gt;*pa&lt;/code&gt; will give you a pointer to &lt;code&gt;int&lt;/code&gt;, and finally &lt;code&gt;**pa&lt;/code&gt; will give you an &lt;code&gt;int&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a;
int *pa = &amp;amp;a; // pointer to int
int **ppa = &amp;amp;pa; // pointer to pointer to int
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can have pointers to array too. But before that, remember &lt;code&gt;[]&lt;/code&gt; has higher precedence than &lt;code&gt;*&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int (*pa)[3]; // pointer to array of 3 elements
int *pa[3]; // 3 element array of pointer to int
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What's the difference between pointer to an array and normal pointer? Consider&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int arr[3] = {1, 2, 3};
int (*pa)[3] = &amp;amp;arr;
int *pb = arr;

printf("arr = %p\n", arr);
printf("pa = %p\n", pa);
printf("pb = %p\n", pb);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arr = 0x7fff2632cc84
pa = 0x7fff2632cc84
pb = 0x7fff2632cc84
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So, essentially they all point to the same location. And we already know &lt;code&gt;arr&lt;/code&gt; is the same as a pointer to the first element. Now we see that &lt;code&gt;&amp;amp;arr&lt;/code&gt; also contains the location of the first element.&lt;/p&gt;

&lt;p&gt;Although &lt;code&gt;pa&lt;/code&gt; and &lt;code&gt;pb&lt;/code&gt; point to the same location, what they point to is very different. &lt;code&gt;pb&lt;/code&gt; is a pointer to [int] so it points to a [int] which is the first element of &lt;code&gt;arr&lt;/code&gt; whereas &lt;code&gt;pa&lt;/code&gt; is a pointer to [array of 3 elements] so it points to an [array of 3 elements] i. e. the whole &lt;code&gt;arr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is evident when you try to do arithmetic - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;printf("pa + 1 = %p\n", pa + 1);
printf("pb + 1 = %p\n", pb + 1);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pa + 1 = 0x7fff2632cc90
pb + 1 = 0x7fff2632cc88
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;pb&lt;/code&gt; is a pointer to &lt;code&gt;int&lt;/code&gt;. So &lt;code&gt;pb+1&lt;/code&gt; points to the next &lt;code&gt;int&lt;/code&gt; 4 bytes after. Whereas &lt;code&gt;pa&lt;/code&gt; is a pointer to array of 3 &lt;code&gt;int&lt;/code&gt;. So &lt;code&gt;pa+1&lt;/code&gt; will point to the next array of 3 &lt;code&gt;int&lt;/code&gt; which is &lt;code&gt;3 * 4 = 12&lt;/code&gt; bytes after, and indeed, &lt;code&gt;pa+1&lt;/code&gt; is 12 bytes after &lt;code&gt;pa&lt;/code&gt;. ( 0x7fff2632cc90 - 0x7fff2632cc84 = 12, these are in hexadecimal in case you're confused).&lt;/p&gt;

&lt;p&gt;You can use a pointer to array just like a normal variable. Just remember the precedence - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int arr[3] = {1, 2, 3};
int (*pa)[3] = &amp;amp;arr;

int a = *pa[1]; // Wrong
int b = (*pa)[1]; // Correct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The easiest way to remember is "Declaration follows usage." So the usage of a pointer will look like the way it was defined. Since we defined &lt;code&gt;pa&lt;/code&gt; as &lt;code&gt;(*pa)[]&lt;/code&gt;, its usage will also look the same.&lt;/p&gt;

&lt;p&gt;One common mistake that students do, with the fact that arrays decay down to pointers in function parameters is working with multidimensional arrays.&lt;/p&gt;

&lt;p&gt;If you have something like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int arr[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
f(arr);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;you might think since array names decay to pointers in function parameter, an array of array should decay to a pointer to pointer. So you might write the declaration of &lt;code&gt;f&lt;/code&gt; as&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void f(int **m) {
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Unfortunately, this is wrong and will give a warning (but will compile)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main.c:22:8: warning: passing argument 1 of ‘f’ from incompatible pointer type [-Wincompatible-pointer-types]                                                   
main.c:11:5: note: expected ‘int **’ but argument is of type ‘int (*)[4]’
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What happened here? It's easy.&lt;/p&gt;

&lt;p&gt;If an array of [int] decays down to a pointer to [int], what should an array of [array of int] decay down to? Of course a pointer to [array of int]. Remember that the size is also part of arrays type. So, in our case, &lt;code&gt;arr&lt;/code&gt; is an array of [4 element array of int]. So, it decays down to pointer to [4 element array of int].&lt;/p&gt;

&lt;p&gt;So you should write&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void f(int (*m)[4]) {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or, you can just take an array of array&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void f(int m[][4]){
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that only the size of the rightmost column is required in the formal parameters list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pointers and Structures and Unions
&lt;/h3&gt;

&lt;p&gt;Now we move on to &lt;code&gt;struct&lt;/code&gt; and &lt;code&gt;union&lt;/code&gt;. We can have pointers to them too.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct some_struct {
    int p;
    int q;
}

struct some_struct a;
struct some_struct *pa = &amp;amp;a;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or if you prefer a &lt;code&gt;typedef&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;typedef struct some_struct {
    int p;
    int q;
} some_struct;

some_struct a;
some_struct *pa = &amp;amp;a;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;An interesting situation occurs when you want to access members of &lt;code&gt;struct&lt;/code&gt; using pointer. Suppose you want to access the member &lt;code&gt;p&lt;/code&gt; through &lt;code&gt;pa&lt;/code&gt;. You might do&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int k = *pa.p;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Except, this doesn't do what you expect. The operator &lt;code&gt;.&lt;/code&gt; has higher precedence than &lt;code&gt;*&lt;/code&gt; so &lt;code&gt;*pa.p&lt;/code&gt; is same as &lt;code&gt;*(pa.p)&lt;/code&gt;. So instead of dereferencing &lt;code&gt;pa&lt;/code&gt; and then accessing the member &lt;code&gt;p&lt;/code&gt;, you end up accessing the member &lt;code&gt;p&lt;/code&gt; and then dereferencing it. But &lt;code&gt;pa&lt;/code&gt; doesn't have a member &lt;code&gt;p&lt;/code&gt;. So, it gives a compiler error.&lt;/p&gt;

&lt;p&gt;Instead, you want to write this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int k = (*pa).p;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Which works the way you want. But writing this is tedious, and turns out that we write this so much that they have a special operator &lt;code&gt;-&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int k = pa -&amp;gt; p;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;pa -&amp;gt; p&lt;/code&gt; is same as &lt;code&gt;(*pa).p&lt;/code&gt; but looks neat and clean.&lt;/p&gt;

&lt;p&gt;The case of unions is a little bit involved. Quoting cppreference - &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A pointer to a union can be cast to a pointer to each of its members (if a union has bit field members, the pointer to a union can be cast to the pointer to the bit field's underlying type). Likewise, a pointer to any member of a union can be cast to a pointer to the enclosing union.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What it means is that, if you have a pointer to a union, you can cast it to any of its members, and vice versa. Take a look&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;typedef union some_union {
        int p;
        char q;
    } some_union;

some_union a = {1}; // Initialize a with p = 1
some_union *pa = &amp;amp;a;
printf("%d\n", pa -&amp;gt; p); // Access p through pointer to a

int * pb = (int *) pa; // cast pa to point to p directly
printf("%d\n", *pb);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here, I could cast the pointer to &lt;code&gt;a&lt;/code&gt; to an &lt;code&gt;int*&lt;/code&gt; and it automatically pointed to the member &lt;code&gt;p&lt;/code&gt;. Similarly, if I had cast it to &lt;code&gt;char*&lt;/code&gt; it would point to &lt;code&gt;q&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Conversely, if I had a pointer to &lt;code&gt;p&lt;/code&gt;, I could cast it to a pointer to &lt;code&gt;some_union&lt;/code&gt; and it would point to &lt;code&gt;a&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int *pc = &amp;amp;(a.p);
some_union *pd = (some_union *)pc;
a.q = 'a';
printf("%c\n", pd -&amp;gt; q);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints &lt;code&gt;a&lt;/code&gt; as expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ministry of Pointer Magic
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pointers and Function
&lt;/h3&gt;

&lt;p&gt;It is possible to have pointers to functions too. Remember that the &lt;strong&gt;return type, the number of parameters it takes, and the type of each parameter&lt;/strong&gt; - these 3 are parts of the type of a function. Hence you must provide these during pointer declaration. Also worth noting &lt;code&gt;()&lt;/code&gt; has higher precedence than &lt;code&gt;*&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int *f(); // a function that returns a pointer to int
int (*f)(); // a pointer to a function that takes no argument and returns an int
int (*f)(int); // a pointer to a function that takes an int and returns an int
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A pointer to function can be used just like other pointers - &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int f(int a) {
    return a+1;
}

int main()
{
    int (*fp)(int) = &amp;amp;f;
    printf("%d\n", (*fp)(1));
    // printf("%d\n", *fp(1)); Wrong~ Won't cpmpile
    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This prints &lt;code&gt;2&lt;/code&gt; as you'd expect.&lt;/p&gt;

&lt;p&gt;Remember I talked about declaration follows usage? Well, turns out that in the case of pointer to functions, that rule can be ignored. For example, this works&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;printf("%d\n", (**fp)(1));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And so does this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;printf("%d\n", (*********fp)(1));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And weirdly enough, this too&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;printf("%d\n", fp(1));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So, in the case of functions, &lt;strong&gt;not only you can dereference as many times as you want, you can drop the dereferencing altogether and just use the pointer as if it were a function itself&lt;/strong&gt;. Another one of those C quirks.&lt;/p&gt;

&lt;p&gt;Finally, you can get wild with pointers and arrays and function like&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;char *(*(*foo)[5])(int); // foo is a pointer to array of 5 elements of pointer to a function that takes an int and returns a pointer to char
int *(*foo)(int *, int (*[4])()); // foo is a pointer to function (that takes a pointer to int and a 4 element array of pointer to functions that return int) and returns a pointer to int
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You get the idea. Yes, it can get pretty messy, but once you know the syntax, and you have &lt;a href="//__GHOST_URL__/p/7a2aaf27-dbd2-4822-8dd4-3bbb6da203ac/cdecl.org"&gt;cdecl&lt;/a&gt;, you can easily breeze through them (or read my &lt;a href="https://dev.to__GHOST_URL__/declaration-in-c-p1/"&gt;article&lt;/a&gt;) &lt;/p&gt;

&lt;p&gt;As for how you can use a function pointer. here's an example of a simple calculator&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

float add(float a, float b) { return a+b; }
float sub(float a, float b) { return a-b; }
float mul(float a, float b) { return a+b; }
float divide(float a, float b) { return a/b; }


int main()
{
    float (*arr[4])(float, float) = { add, sub, mul, divide };
    int n;
    float a, b;
    printf("Enter two numbers: ");
    scanf("%f%f", &amp;amp;a, &amp;amp;b);
    printf("Enter 1 for addition, 2 for subtraction, 3 for multiplication, 4 for division: ");
    scanf("%d", &amp;amp;n);
    printf("%f", arr[n-1](a, b));

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We are storing all 4 operations in an array and when the user enters a number, we call the corresponding operation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Qualified types
&lt;/h3&gt;

&lt;p&gt;Each type in C can be qualified by using qualifiers. In particular we have 3 - &lt;code&gt;const&lt;/code&gt;, &lt;code&gt;volatile&lt;/code&gt;, and &lt;code&gt;restrict&lt;/code&gt;. Here we will look at &lt;code&gt;const&lt;/code&gt; and &lt;code&gt;restrict&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Adding &lt;code&gt;const&lt;/code&gt; to a type effectively marks it read-only, so that attempting to change the value will result in a compiler error.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const int p = 1;
p = 2; // error: assignment of read-only variable ‘p’
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Turns out, &lt;code&gt;int const&lt;/code&gt; and &lt;code&gt;const int&lt;/code&gt; both are valid. Now if I throw pointers into the party, I get some fun stuff&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const int *p;
int * const p;
const int * const p;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Can you guess which one is what?&lt;/p&gt;

&lt;p&gt;To untangle this, we will remember &lt;code&gt;some_data_type *p&lt;/code&gt; declares &lt;code&gt;p&lt;/code&gt; to be a pointer to &lt;code&gt;some_data_type&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Hence, &lt;code&gt;const int *p&lt;/code&gt; can be thought of as &lt;code&gt;(const int) *p&lt;/code&gt;. So that &lt;code&gt;p&lt;/code&gt; is a pointer to a &lt;code&gt;const int&lt;/code&gt;. It means, whatever &lt;code&gt;p&lt;/code&gt; is pointing to is a &lt;code&gt;const int&lt;/code&gt; and you cannot change that. However, &lt;code&gt;p&lt;/code&gt; itself is not &lt;code&gt;const&lt;/code&gt; and can be changed.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const int a = 1;
const int b = 2;
const int *p = &amp;amp;a;

p = &amp;amp;b; // works. You can change p
// *p = 3; error: assignment of read-only location ‘*p’
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For the second one compare it with &lt;code&gt;int const p&lt;/code&gt; which declares &lt;code&gt;p&lt;/code&gt; as a read only &lt;code&gt;int&lt;/code&gt;. So, &lt;code&gt;int * const p&lt;/code&gt; should declare &lt;code&gt;p&lt;/code&gt; as read-only &lt;code&gt;int*&lt;/code&gt;. This means the pointer itself is &lt;code&gt;const&lt;/code&gt; and you can't change &lt;code&gt;p&lt;/code&gt; , but you can change what &lt;code&gt;p&lt;/code&gt; is pointing to.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;int a = 1, b = 2;
int * const p = &amp;amp;a;

*p = 2; // Works. you can change *p
// p = &amp;amp;b; error: assignment of read-only location ‘p’
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And finally, &lt;code&gt;const int * const p&lt;/code&gt; declares that both &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;*p&lt;/code&gt; are read-only. So you can neither change &lt;code&gt;p&lt;/code&gt; nor &lt;code&gt;*p&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const int a = 1, b = 2;
const int * const p = &amp;amp;a;
// *p = 2; error: assignment of read-only location ‘*p’
// p = &amp;amp;b; error: assignment of read-only location ‘p’
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now let's look at the &lt;code&gt;restrict&lt;/code&gt; keyword. The &lt;code&gt;restrict&lt;/code&gt; keyword, when applied to a pointer &lt;code&gt;p&lt;/code&gt;, tells the compiler that as long as &lt;code&gt;p&lt;/code&gt; is in scope, only &lt;code&gt;p&lt;/code&gt; and pointers directly or indirectly derived from it (e. g. &lt;code&gt;p+1&lt;/code&gt; ) will access the thing it's pointing to.&lt;/p&gt;

&lt;p&gt;Confused? Let's see an example.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void f(int *p, int *q, int *v) {
    *p += *v;
    *q += *v;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is the assembly generated after enabling optimization -&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mov     eax, DWORD PTR [rdx]
add     DWORD PTR [rdi], eax
mov     eax, DWORD PTR [rdx]
add     DWORD PTR [rsi], eax
ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The problem in this function is, &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;q&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt; might point to the same location. So that when you do &lt;code&gt;*p += *v&lt;/code&gt;, it might happen that &lt;code&gt;*v&lt;/code&gt; also gets changed because &lt;code&gt;p&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt; were pointing to the same location. &lt;/p&gt;

&lt;p&gt;This is why &lt;code&gt;*v&lt;/code&gt; is first loaded into &lt;code&gt;eax&lt;/code&gt; by &lt;code&gt;mov     eax, DWORD PTR [rdx]&lt;/code&gt;. Then it is added to &lt;code&gt;*p&lt;/code&gt;. Again, we have to load &lt;code&gt;*v&lt;/code&gt; because at this point, we are not sure if &lt;code&gt;*v&lt;/code&gt; has changed or not.&lt;/p&gt;

&lt;p&gt;Now if I update the function as follows -&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void g(int *restrict p, int *restrict q, int *restrict v) {
    *p += *v;
    *q += *v;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;the compiler is free to assume that &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;q&lt;/code&gt; and &lt;code&gt;v&lt;/code&gt; all point to different locations, and can load &lt;code&gt;*v&lt;/code&gt; only once, and indeed it does&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mov     eax, DWORD PTR [rdx]
add     DWORD PTR [rdi], eax
add     DWORD PTR [rsi], eax
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that it is up to the programmer to guarantee that the pointers do not overlap. In case they do, it is undefined behavior.&lt;/p&gt;

&lt;p&gt;You can read more about &lt;code&gt;restrict&lt;/code&gt;&lt;a href="https://en.cppreference.com/w/c/language/restrict" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That's probably enough for one post. To quickly recap, you have learned - &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What pointers are.&lt;/li&gt;
&lt;li&gt;How to declare and dereference pointers.&lt;/li&gt;
&lt;li&gt;Pointer arithmetic.&lt;/li&gt;
&lt;li&gt;Pointer comparison.&lt;/li&gt;
&lt;li&gt;Pointer to array and array of pointers.&lt;/li&gt;
&lt;li&gt;Pointers and arrays are not the same.&lt;/li&gt;
&lt;li&gt;Pointer arithmetic and array indexing are equivalent.&lt;/li&gt;
&lt;li&gt;Array in function parameter decay to a pointer.&lt;/li&gt;
&lt;li&gt;Pointers to a multidimensional array.&lt;/li&gt;
&lt;li&gt;Pointers to structures and unions.&lt;/li&gt;
&lt;li&gt;Pointer to functions.&lt;/li&gt;
&lt;li&gt;Pointer to wild exotic types.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;const&lt;/code&gt; and &lt;code&gt;restrict&lt;/code&gt; with pointers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that most of these hold true in C++ also, although you have minor changes, and some new stuff like smart pointers.&lt;/p&gt;

&lt;p&gt;Hopefully, you learned something new in this post. Subscribe to &lt;a href="https://blog.abhattacharyea.dev" rel="noopener noreferrer"&gt;my blog&lt;/a&gt; for more.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>c</category>
      <category>pointers</category>
    </item>
    <item>
      <title>Replace your Existing Unix Utilities with These Modern Alternatives</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Fri, 30 Jul 2021 17:34:34 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/replace-your-existing-unix-utilities-with-these-modern-alternatives-2bfo</link>
      <guid>https://dev.to/heraldofsolace/replace-your-existing-unix-utilities-with-these-modern-alternatives-2bfo</guid>
      <description>&lt;p&gt;In your day-to-day Unix or Linux usage you use different tools like &lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;grep&lt;/code&gt;, or &lt;code&gt;find&lt;/code&gt;. These are ubiquitous utilities found in every Nix system. However, they were created a long time ago, when computers lacked computational power and speed. These tools were tailored towards systems with very little resources. Nowadays, our systems are far better and our requirements are far bigger. In this article, I will show you some utilities aimed towards providing modern replacements for these useful Unix utilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;&lt;code&gt;bat&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bat&lt;/code&gt; is a modern replacement for cat written in Rust. Unlike &lt;code&gt;cat&lt;/code&gt;, this tool supports syntax highlighting for many programming languages out of the box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvkj5sya46iphvlwzumv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvkj5sya46iphvlwzumv.png" alt="bat displaying Markdown file" width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftnpqt0rsoot98ziz8kr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftnpqt0rsoot98ziz8kr5.png" alt="bat displaying Ruby file" width="742" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another interesting feature of &lt;code&gt;bat&lt;/code&gt; is native Git integration. If you run &lt;code&gt;bat&lt;/code&gt; from a git repository, you will get a "git gutter" indicating modification with respect to the index.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvx6octofh5e7e8q4wwvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvx6octofh5e7e8q4wwvs.png" alt="Git gutter with bat" width="797" height="660"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, &lt;code&gt;bat&lt;/code&gt; automatically pipes its own output into a pager (e. g. &lt;code&gt;less&lt;/code&gt;) when the output is bigger than one page. Which means you don't have to manually pipe the output into &lt;code&gt;less&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you wish to replace &lt;code&gt;cat&lt;/code&gt; with &lt;code&gt;bat&lt;/code&gt;, you can use the following alias -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias cat='bat --style header --style rules --style snip --style changes --style header'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a href="https://github.com/ogham/exa" rel="noopener noreferrer"&gt;&lt;code&gt;exa&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;exa&lt;/code&gt; is a modern alternative for &lt;code&gt;ls&lt;/code&gt;, also written in Rust. Apart from some minor differences, &lt;code&gt;exa&lt;/code&gt; has all the features of &lt;code&gt;ls&lt;/code&gt;, along with Git integration and colors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cdq1e9q0ih7q8kkb9j5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9cdq1e9q0ih7q8kkb9j5.png" alt="exa uses colors to distinguish files and folders" width="606" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;--git&lt;/code&gt; option, you can quickly see the Git status of files inside a Git repo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58f4ay3qz9xiof6pr8qq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F58f4ay3qz9xiof6pr8qq.png" alt="exa shows the Git status" width="799" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;--tree&lt;/code&gt; option to get a tree view of the files. Using the &lt;code&gt;--level&lt;/code&gt; option, you can specify the depth of the tree.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr2kch361oe3jz4w4mtqh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr2kch361oe3jz4w4mtqh.png" alt="exa tree view" width="511" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;--icons&lt;/code&gt; option to display little icons next to the filenames, provided your font contains those icons. You can install any Nerd Font and you should be good to go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqeghvv1k99m99w2ey3y1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqeghvv1k99m99w2ey3y1.png" alt="exa displaying icons" width="788" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/bootandy/dust" rel="noopener noreferrer"&gt;&lt;code&gt;dust&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;dust&lt;/code&gt; is an alternative for &lt;code&gt;du&lt;/code&gt;, and it should not be surprising that it is written in Rust! When you run dust, it shows a nice tree structure of files and folders along with a visualization of sizes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjphywg17m5b923p5v9ih.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjphywg17m5b923p5v9ih.png" alt="Output of dust" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/muesli/duf" rel="noopener noreferrer"&gt;&lt;code&gt;duf&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;duf&lt;/code&gt; is a replacement for the &lt;code&gt;df&lt;/code&gt; utility. Guess which language it is written in? Nope, not Rust. It's written in Go.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;duf&lt;/code&gt; makes the output easy to understand and visualize. It has color highlights and percentage bars so that you can easily understand your disk usage. Also, it separates local devices, network devices, and special devices in separate regions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9tr1o7tgdixdrfucrij.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu9tr1o7tgdixdrfucrij.png" alt="duf output" width="800" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;duf --all&lt;/code&gt; to list all devices including pseudo, duplicate and inaccessible devices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30680441sma6pxtee1rp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30680441sma6pxtee1rp.png" alt="duf --all output" width="647" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use &lt;code&gt;duf --json&lt;/code&gt; to output a convenient JSON file.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/sharkdp/fd" rel="noopener noreferrer"&gt;&lt;code&gt;fd&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;fd&lt;/code&gt; is an alternative for the find utility. And we're back again to the Rust world!&lt;/p&gt;

&lt;p&gt;When you run fd command with a search pattern, it will search the current directory for the pattern and show you the file list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfn5zv2bd5c7cv0musqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfn5zv2bd5c7cv0musqp.png" alt="fd output" width="471" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also use regular expression to refine your search.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapocx532f0m5jf03olcg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapocx532f0m5jf03olcg.png" alt="Regex with fd" width="219" height="78"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can search for a specific extension by using the &lt;code&gt;-e&lt;/code&gt; flag. You can combine this with the search pattern.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q2smnyfwlfk9h5dtmj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q2smnyfwlfk9h5dtmj3.png" alt="fd with -e flag" width="555" height="670"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just like find, you can run a command on every found file using the &lt;code&gt;-X&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1znlrmluuchxvjc4yhut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1znlrmluuchxvjc4yhut.png" alt="exa with -X flag" width="800" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/BurntSushi/ripgrep" rel="noopener noreferrer"&gt;&lt;code&gt;ripgrep&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ripgrep&lt;/code&gt; is a super fast &lt;code&gt;grep&lt;/code&gt; alternative written in Rust. Apart from being super fast, it also respects your &lt;code&gt;.gitignore.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ripgrep&lt;/code&gt; is invoked through the &lt;code&gt;rg&lt;/code&gt; command. The basic usage is similar to &lt;code&gt;grep&lt;/code&gt;. You provide a pattern and a file to find the pattern in. &lt;code&gt;rg&lt;/code&gt; by default also shows the line numbers and highlights the search term.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h6yf6s9uvd31byp7zql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1h6yf6s9uvd31byp7zql.png" alt="rg searching for a pattern" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rg&lt;/code&gt; also supports regular expressions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F530mw59cp8gspf8qsffl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F530mw59cp8gspf8qsffl.png" alt="Regex in rg" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you do not specify a file name, &lt;code&gt;rg&lt;/code&gt; will perform a recursive search. Similar to &lt;code&gt;grep -r&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzzxze3y2iet0g5yjx94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzzxze3y2iet0g5yjx94.png" alt="Recursive search with rg" width="675" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rg&lt;/code&gt; respects your &lt;code&gt;.gitignore&lt;/code&gt;. Which means if a file is matched in &lt;code&gt;.gitignore&lt;/code&gt;, it will not be searched. You can read more about how to use this feature in the &lt;a href="https://github.com/BurntSushi/ripgrep/blob/master/GUIDE.md#automatic-filtering" rel="noopener noreferrer"&gt;manual&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/tldr-pages/tldr" rel="noopener noreferrer"&gt;&lt;code&gt;tldr&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;How many times have you forgot how a command works, and opened the man pages only to get buried under tons and tons of documentation, when you only wanted some examples? &lt;code&gt;tldr&lt;/code&gt; is not an exact replacement for man pages, but it is a community effort to simplify the manual pages. Rather than throwing an ocean of information about you, &lt;code&gt;tldr&lt;/code&gt; lists examples and small details, enough to get you started.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyl6mhgi7g9gr4zsau9vr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyl6mhgi7g9gr4zsau9vr.png" alt="tldr tar output" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/dalance/procs" rel="noopener noreferrer"&gt;&lt;code&gt;procs&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Yet another tool written in Rust. &lt;code&gt;procs&lt;/code&gt; is a modern alternative for &lt;code&gt;ps&lt;/code&gt;. When you run &lt;code&gt;procs&lt;/code&gt;, it shows you a list of running processes in a nicely themed output. The output is automatically paged.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdkdf94wwo4jshrqo8wd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgdkdf94wwo4jshrqo8wd.png" alt="procs output" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run &lt;code&gt;procs&lt;/code&gt; followed by the name of a process, &lt;code&gt;procs&lt;/code&gt; will search for that process, similar to running &lt;code&gt;ps aux | grep process&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxda4uetzvalnnvi68e5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhxda4uetzvalnnvi68e5.png" alt="procs can search processes" width="800" height="113"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;procs&lt;/code&gt; can also show other information which are not available to &lt;code&gt;ps&lt;/code&gt;, like TCP/UDP port, Read/Write throughput, or Docker container name. You can read more about them in the procs manual.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://github.com/ajeetdsouza/zoxide" rel="noopener noreferrer"&gt;&lt;code&gt;zoxide&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;zoxide&lt;/code&gt; is a fast replacement for the &lt;code&gt;cd&lt;/code&gt; command. It keeps track of the directories you visit and can quickly take you to the directory you want to go.&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;zoxide&lt;/code&gt; just like the regular &lt;code&gt;cd&lt;/code&gt; command. For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;z foo/bar/baz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;zoxide&lt;/code&gt; keeps a track of the directories you visit, and you can use fuzzy finding to quickly visit a directory you have visited before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;z baz # Takes you the highest ranking directory matching baz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;zoxide&lt;/code&gt; also comes with various integrations with many different tools. Check them out from their &lt;a href="https://github.com/ajeetdsouza/zoxide#third-party-integrations" rel="noopener noreferrer"&gt;manual&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope you could find your next favourite command line utility from this post :). If you liked this article, do not forget to share and subscribe to &lt;a href="https://blog.abhattacharyea.dev" rel="noopener noreferrer"&gt;my blog&lt;/a&gt; for more such articles. &lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>shell</category>
      <category>cli</category>
    </item>
    <item>
      <title>Help needed in figuring out Rails association</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Sat, 06 Jun 2020 08:40:47 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/help-needed-in-figuring-out-rails-association-540j</link>
      <guid>https://dev.to/heraldofsolace/help-needed-in-figuring-out-rails-association-540j</guid>
      <description>&lt;p&gt;I have two models - &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Network&lt;/code&gt;. In my case, each &lt;code&gt;User&lt;/code&gt; can have many &lt;code&gt;Network&lt;/code&gt; and each &lt;code&gt;Network&lt;/code&gt; can have many &lt;code&gt;User&lt;/code&gt; and belongs to one &lt;code&gt;User&lt;/code&gt; (the one who created).&lt;/p&gt;

&lt;p&gt;For example, let's say there is &lt;code&gt;user1&lt;/code&gt; with two networks &lt;code&gt;N1&lt;/code&gt; and &lt;code&gt;N2&lt;/code&gt;. The network &lt;code&gt;N1&lt;/code&gt; has &lt;code&gt;user2&lt;/code&gt; and the network &lt;code&gt;N2&lt;/code&gt; has &lt;code&gt;user3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What I want to achieve is the &lt;code&gt;creator&lt;/code&gt; field of &lt;code&gt;N1&lt;/code&gt; and &lt;code&gt;N2&lt;/code&gt; will give me &lt;code&gt;user1&lt;/code&gt; and the &lt;code&gt;users&lt;/code&gt; field of &lt;code&gt;N1&lt;/code&gt; will give me &lt;code&gt;[user2]&lt;/code&gt; and that of &lt;code&gt;N2&lt;/code&gt; will give me &lt;code&gt;[user3]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So far, it's ok. I can have the &lt;code&gt;creator&lt;/code&gt; field with &lt;code&gt;foreign_key&lt;/code&gt;, and I can use a &lt;code&gt;has_many through&lt;/code&gt; relation to figure out which networks contain which users and which users belong to which networks. &lt;/p&gt;

&lt;p&gt;But in that case, is there a way to quickly tell if there is a relation between two users or not?&lt;/p&gt;

&lt;p&gt;For example something like &lt;code&gt;user2.follows? user1&lt;/code&gt; will return &lt;code&gt;true&lt;/code&gt; since &lt;code&gt;user2&lt;/code&gt; belongs to a network of &lt;code&gt;user1&lt;/code&gt;. I can iterate through all the networks created by &lt;code&gt;user1&lt;/code&gt; and check if &lt;code&gt;user2&lt;/code&gt; belongs to it, but is there a better way?&lt;/p&gt;

</description>
      <category>help</category>
      <category>ruby</category>
      <category>rails</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Safety App - a Project I made as a Student</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Fri, 22 May 2020 09:21:09 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/safety-app-a-project-i-made-as-a-student-5bc7</link>
      <guid>https://dev.to/heraldofsolace/safety-app-a-project-i-made-as-a-student-5bc7</guid>
      <description>&lt;p&gt;As you might know, I am currently a student. As a mathematics undergraduate in the final year, Covid 19 has been a major setback. Recently, I found out about the &lt;a href="https://github.com/education/graduation/pulls" rel="noopener noreferrer"&gt;yearbook initiative by Github Education&lt;/a&gt; and wanted to share a project of mine as a student.&lt;/p&gt;

&lt;p&gt;It's a little difficult for me to consistently work on a project. Apart from studying and teaching students I also work as a part time web developer in DataSutram. Anyway this one had a personal interest.&lt;/p&gt;

&lt;p&gt;Last year one of my friends told me about a kidnapping in her area and it scared me to my core. I thought if I could do anything to help it would be nice. So I made the &lt;a href="https://play.google.com/store/apps/details?id=dev.abhattacharyea.safety" rel="noopener noreferrer"&gt;Safety App&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;It's a pretty simple application. It's main feature is that it lets you select your &lt;strong&gt;trusty&lt;/strong&gt; contacts and makes it easier to contact them during emergency.&lt;/p&gt;

&lt;p&gt;You can choose as many contacts as you want.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1o9anfakc6tzt593r4r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1o9anfakc6tzt593r4r.png" alt="contacts" width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app shows two persistent notifications&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk14ss6un6cof59fk6goz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk14ss6un6cof59fk6goz.jpg" alt="notification" width="800" height="1663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first one has 3 buttons. The first button is for making calls. If you click on it you will be shown a dialog with a list of your trusty contacts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqau9tqx4le2338k7sg43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqau9tqx4le2338k7sg43.png" alt="contacts" width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click any of them, it will call the contact. The same with 2nd button except it launches the SMS app where you can send them an SMS.&lt;/p&gt;

&lt;p&gt;The third button is the most important one - SOS. When you press it, it will send an SOS to &lt;strong&gt;every&lt;/strong&gt; trusted contact with your location.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw5j0wnj81xrygqaacp3u.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw5j0wnj81xrygqaacp3u.jpg" alt="SOS" width="800" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Observe that in every screenshot the first contact is called "Super Trusty Contact". It's because it is a special contact. How?&lt;/p&gt;

&lt;p&gt;These notifications are visible in the lockscreen too. When your screen is locked, there is no way to show you which contact to pick. There it picks the first contact. That's why it's super trusty. Although, the SOS os &lt;strong&gt;always&lt;/strong&gt; sent to &lt;strong&gt;every&lt;/strong&gt; contact.&lt;/p&gt;

&lt;p&gt;As for the 2nd notification, it's like the SOS but it lets you capture an audio and uploads it to the cloud and sends a link to your contacts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0zveud49ztmoqhoc8wxa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0zveud49ztmoqhoc8wxa.jpg" alt="Alt Text" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is also a map in the app which shows you locations of nearby police stations, hospitals etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fca58p8dblphtmwcj284q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fca58p8dblphtmwcj284q.png" alt="map" width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click one of them, you can get at a glance a few infos and also a basic route.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9ceov2uji8p2a2kj2amv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9ceov2uji8p2a2kj2amv.jpg" alt="map" width="800" height="1663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1apepynv9inczqhncfqb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1apepynv9inczqhncfqb.png" alt="route" width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I made it
&lt;/h2&gt;

&lt;p&gt;The app is written in Kotlin and open sourced &lt;a href="https://github.com/Herald-Of-Solace/Safety-App" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Code wise this is not the best thing I have made. There are repetition everywhere, redundant codes and buggy stuff, but I wrote it in a hurry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things to do
&lt;/h2&gt;

&lt;p&gt;Currently there are a few bugs. The notifications don't show up in the lockscreen for some phones. The notifications might disappear. Sometimes the app also crashes and sometimes the location cannot be fetched.&lt;/p&gt;

&lt;p&gt;I'm working on those issues and it's really difficult managing time for everything. Hopefully I'll get around to fixing them soon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;The app is free forever and ad-free. This is an app that is meant to help people and I do not want to profit off ot. But still, the cloud is costly and I'm just a student managing my studies. If you find the app useful, and want to support development, you can donate from the app. You can also Paypal me at &lt;a href="mailto:aniketmail669@gmail.com"&gt;aniketmail669@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;P. S. Our state just faced the fiercest super cyclone since 1737 and we're in debris. It's been 48 hours and we still have no electricity and we've run out of water. So please #PrayForBengal and let's hope we can turn around.&lt;/p&gt;

</description>
      <category>octograd2020</category>
      <category>github</category>
      <category>android</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>My Journey With Linux (September 2015 to present)</title>
      <dc:creator>Aniket Bhattacharyea</dc:creator>
      <pubDate>Thu, 25 Apr 2019 15:17:23 +0000</pubDate>
      <link>https://dev.to/heraldofsolace/my-journey-with-linux-september-2015-to-present-ieo</link>
      <guid>https://dev.to/heraldofsolace/my-journey-with-linux-september-2015-to-present-ieo</guid>
      <description>&lt;p id="3f31"&gt;(This is a repost from my blog which I thought would be a good place to post here)&lt;br&gt;&lt;br&gt;Linux — the first thing that comes to mind when you hear this word is the image of a nerdy, bearded person, running commands after commands like a blaze.&lt;/p&gt;

&lt;p id="0bcf"&gt;I also had this image, and honestly, I used to be scared of Linux. People around me had (and still have) vague ideas about Linux. Nobody knew what it is, and told me not to try it.&lt;/p&gt;

&lt;p id="6a94"&gt;So, I tried it. And fell in love with it.&lt;/p&gt;

&lt;p id="2356"&gt;This story captures my journey with Linux, starting from 2015, When I got my first computer, to the present time, and to the future. You will see a noob heading towards achieving perfection in this story —&lt;/p&gt;

&lt;p id="3713"&gt;&lt;em&gt;P.S: After trying Linux, I am really a nerdy, bearded guy, typing commands after commands (not like a blaze).&lt;/em&gt;&lt;/p&gt;

&lt;h2 id="e109"&gt;
&lt;strong&gt;Reasons for trying it ou&lt;/strong&gt;t&lt;/h2&gt;

&lt;p id="61e3"&gt;I wanted to make a custom ROM for my phone in 2015. On XDA, all the guides used Linux. I was a Windows lover back then (yes, I was) and had zero experience with Linux. Then I found out about Cygwin Linux emulator. But the XDA threads said it’s buggy, unstable and it installs about 1gb of software. I didn’t have unlimited internet back then, and Ubuntu was almost of the same size. So I thought if both of them are the same size, why not download the stable one? After all, I’m all about learning.&lt;/p&gt;




&lt;h2 id="8d3e"&gt;&lt;strong&gt;First try — Puppy Linux&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="28e7"&gt;I decided to go for Ubuntu (Sep 2015), but noticed that I didn’t have enough data. So, searching for something light, I came across Puppy Linux (250mb) and decided to try it.&lt;/p&gt;

&lt;p id="1b3a"&gt;The installation of Puppy had instructions about “full” and “frugal” installation, which were beyond my understandings at that moment. Nevertheless, booted up the live CD, and in order to create a partition, opened GParted. I knew nothing about filesystems and formatted the whole drive as ext4. The result? My windows won’t boot up. Tried to reinstall Windows, but the installation disk would not list any partition. Somehow I managed to format the drives as NTFS and installed Windows 7.&lt;/p&gt;

&lt;p id="921d"&gt;&lt;strong&gt;What I learned —&lt;/strong&gt;&lt;/p&gt;

&lt;p id="9a45"&gt;Never try to mess with your partitions without having a vast idea.&lt;/p&gt;




&lt;h2 id="4618"&gt;&lt;strong&gt;2nd try- Ubuntu&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="ce55"&gt;Oct 2015, decided to go again. This time, Ubuntu Vivid Vervet 15.04&lt;/p&gt;

&lt;p id="802c"&gt;This time, everything went fine and set up a working installation. It was a bit uncomfortable first, especially the buttons being on the left side of the toolbar. But I grew accustomed with time. Still, I preferred Windows. Some things (like installing apps via CLI) were new to me, and not having enough knowledge resulted in a lot of things not working. But I didn’t lose patience and kept learning.&lt;/p&gt;

&lt;p id="3d9d"&gt;&lt;strong&gt;What I learned —&lt;/strong&gt;&lt;/p&gt;

&lt;p id="ff5a"&gt;Basics of Linux, simple terminal commands.&lt;/p&gt;




&lt;h2 id="c021"&gt;&lt;strong&gt;3rd try- Kali Linux&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="bdce"&gt;Feb 2016, replaced Ubuntu with Kali Linux 2016.1 rolling. This is the distro which made me fall in love with Linux. Meanwhile, windows started showing some bugs. So I started spending more and more time with Linux. Using Kali Linux, I even built my Linux from scratch. Also, the troubleshooting in kali gave me a deep knowledge about Linux.&lt;/p&gt;

&lt;p id="6c22"&gt;&lt;strong&gt;What I learned —&lt;/strong&gt;&lt;/p&gt;

&lt;p id="ca17"&gt;More commands and concepts of Linux.&lt;/p&gt;




&lt;h2 id="e56b"&gt;&lt;strong&gt;4th try — Linux from Scratch&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="f297"&gt;Using Kali, I made a Linux from Scratch and named it Papiya Linux after my mom. Although it never went public, it gave me a HUGE knowledge.&lt;/p&gt;

&lt;p id="993d"&gt;&lt;strong&gt;What I learned —&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
    &lt;li id="4ff2"&gt;Concepts of Linux — how packages work, how two softwares communicate with each other and the kernel, different states of processes.&lt;/li&gt;
    &lt;li id="18e7"&gt;More commands.&lt;/li&gt;
    &lt;li id="6738"&gt;Use of sed and grep and find.&lt;/li&gt;
    &lt;li id="cae2"&gt;Writing complex bash scripts using if, while, for, head, tee, cut etc.&lt;/li&gt;
    &lt;li id="3520"&gt;Compiling softwares from source.&lt;/li&gt;
    &lt;li id="7b89"&gt;Analyze the output of make and troubleshoot.&lt;/li&gt;
    &lt;li id="a6aa"&gt;Use of aclocal, configue, autoreconf and make.&lt;/li&gt;
    &lt;li id="7ac2"&gt;Vim.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2 id="1581"&gt;&lt;strong&gt;Arch Linux&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="febe"&gt;Since August last year, I have been using Arch as my main OS. Windows is still there. And an empty partition where I try out new OS’s.&lt;/p&gt;

&lt;p id="c5e8"&gt;I really love Arch, because of its super fast pacman, a huge repository, and an awesome Wiki.&lt;/p&gt;

&lt;p id="accd"&gt;However, October last year, I ran into a problem.
I converted my ext4 installation into btrfs. For those who don’t know, btrfs offers a tool to convert from ext4 to btrfs without losing data, and it also creates a backup image in case you want to revert.

&lt;/p&gt;
&lt;p id="cb4a"&gt;After trying btrfs for a week, I felt no need of reverting, so deleted the backup image. Wrong choice!!&lt;/p&gt;

&lt;p id="1a8a"&gt;For some reason I opened Windows, and being Windows, it couldn’t recognize my btrfs partition, and corrupted it. And Arch won’t boot.&lt;/p&gt;

&lt;p id="bc28"&gt;I saved it by using the tools btrfs offers (btrfs — check-extent-tree and btrfs — init-csum), however a few days later, Windows corrupted it again, this time drastically.&lt;/p&gt;

&lt;p id="46fd"&gt;All my executables were corrupted, and it was beyond repair, and my only choice was to reinstall. But I would lose all the data.&lt;/p&gt;

&lt;p id="30df"&gt;My home partition was on the same drive as the root, and if I reinstalled, I would lose everything. But since “reinstall and start from scratch” is so Windows style, I figured out a way.&lt;/p&gt;

&lt;p id="2a66"&gt;I had to save my home directory.&lt;/p&gt;

&lt;p id="3ac5"&gt;So, I booted a live cd, mounted another partition, and rsynced my home directory in this drive. Now I could reinstall the OS. But what about my apps those were installed?&lt;/p&gt;

&lt;p id="cd92"&gt;Well, it’s Linux.&lt;/p&gt;

&lt;p id="abf0"&gt;I found out pacman stores the data about packages in /var/lib/pacman/local. Each package gets its own directory in this format -
&lt;strong&gt;package _ name — version — revision&lt;/strong&gt;
So, I could do this -

&lt;/p&gt;
&lt;pre id="5c4e"&gt;for i in /mnt/var/lib/pacman/local
do
echo $i &amp;gt;&amp;gt; list
done&lt;/pre&gt;
This would create a file called “list” which would contain the list of packages to install, but it would contain the version numbers too, which I don’t want. So, I used Bash’s string splitting to split the string from the second last hyphen. This would get only the package name -
&lt;pre id="c6d7"&gt;for i in /mnt/var/lib/pacman/local
do
i = ${i%- * }
i = ${i% — *}
echo $i &amp;gt;&amp;gt; list
done&lt;/pre&gt;
&lt;p id="c2bb"&gt;&lt;em&gt;Could this be done in any other way? Tell me in the comments.&lt;/em&gt;&lt;/p&gt;

&lt;p id="6a0e"&gt;This created the desired list. The rest was easy, booted the Arch live CD, fed this file to pacman, and pointed pacman to the installation path. After everything was installed, created the user, and restored my home directory, BUT this time to a separate partition, so in case anything goes wrong with the root partition, I can save my home directory.&lt;/p&gt;

&lt;p id="52e8"&gt;&lt;strong&gt;What I learned — &lt;/strong&gt;
1. Never delete backups.
2. Never trust Windows.
3. Always keep your home directory on a separate partition.
4. Use your goddamn brain.
5. Fix your own shit yourself.

&lt;/p&gt;
&lt;p id="4f8a"&gt;In April this year, I screwed up once again. I decided to ditch Windows and Fedora completely and make Arch the only OS. Also, I wanted to clean up my HDD, which was one hell of a mess of partitions.&lt;/p&gt;

&lt;p id="d338"&gt;So, I rsynced my root and home partition to another NTFS partition and by the time I realised my mistake, it was too late. Yes, you can guess what happened. As I backed up to NTFS, I lost all the permissions. All the files were set to rwxrwxrwx.&lt;/p&gt;

&lt;p id="095e"&gt;After banging my head on the desk for a few hours, I remembered pacman can check for file integrity. So, it must store the data somewhere. After looking at the database (/var/lib/pacman/local/) I found out pacman stores the metadata in a mtree file under package directories. These mtree files are nothing but a gzipped data file.&lt;/p&gt;

&lt;p id="2a3a"&gt;I tried to restore the permissions with BSD mtree but it gave some errors because the format was not recognised. So, I wrote my own parser in Bash (sorry lost the code). It parsed the permission data from the mtree files, and applied them. There were 5 or 6 edge cases where my coding magic did not work, but I could fix them manually.&lt;/p&gt;

&lt;p id="f95c"&gt;&lt;strong&gt;What I learned —&lt;/strong&gt;&lt;/p&gt;

&lt;p id="63df"&gt;Backups are important, and be sure where you are backing up.&lt;/p&gt;




&lt;h2 id="74f2"&gt;&lt;strong&gt;January 2018&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="8ead"&gt;It’s only been 1 month of 2018, but the Linuxer (is that even a word?) inside me is urging for something better. So I decided to “upgrade” my installation.&lt;/p&gt;

&lt;p id="4dbf"&gt;First thing I noticed, I was still using BIOS and MBR, which was, unknowing to me, chosen by the one who installed Windows on my laptop when I bought it.&lt;/p&gt;

&lt;p id="3275"&gt;My first target was to convert it to EFI and GPT.&lt;/p&gt;

&lt;p id="3e7d"&gt;The first part was easy —&lt;/p&gt;

&lt;ol&gt;
    &lt;li id="827b"&gt;Create an ESP partition.&lt;/li&gt;
    &lt;li id="3ee2"&gt;Mount it to /boot/efi&lt;/li&gt;
    &lt;li id="a073"&gt;Install grub-efi.&lt;/li&gt;
    &lt;li id="9f34"&gt;Update grub files.&lt;/li&gt;
&lt;/ol&gt;

&lt;p id="3661"&gt;The last two parts I had done before, and there was no way I could fuck that up and I did.&lt;/p&gt;

&lt;p id="0c78"&gt;After 1 hour of grub not being recognized, I realised I had to copy grub64.efi to ESP/EFI/Boot/BOOTx64.efi.&lt;/p&gt;

&lt;p id="1228"&gt;After I did so, grub was recognized, but it didn’t boot.&lt;/p&gt;

&lt;p id="553d"&gt;After 2 hours of grub not finding the kernel and initrd files, I found out it was due to an issue with incorrect mount path.&lt;/p&gt;

&lt;p id="6f7e"&gt;After successfully configuring grub, I was satisfied and confident. So I nuked grub, and installed Refind, and got it running in the first try :)&lt;/p&gt;

&lt;p id="2563"&gt;The next part was easier, I just used gdisk to convert MBR to GPT and it worked flawlessly.&lt;/p&gt;

&lt;p id="ec19"&gt;I also made zsh my default shell. After years of loving Bash, I finally moved on to new horizons.&lt;/p&gt;

&lt;p id="68ab"&gt;&lt;strong&gt;What I learned —&lt;/strong&gt;&lt;/p&gt;

&lt;p id="10a4"&gt;Sometimes changes are refreshing.&lt;/p&gt;

&lt;h2 id="271a"&gt;&lt;strong&gt;Rest of 2018&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="1719"&gt;I did not do much of a change in 2018. I got an internship and later a work from home job, so inevitably I resisted to breaking changes. There were few hiccups along the way, but not too challenging.&lt;/p&gt;

&lt;p id="a8dd"&gt;But it doesn’t mean I learned nothing new. I worked as a System administrator and backend developer in my work and had to use Linux extensively. In particular, I learned -&lt;/p&gt;

&lt;ol&gt;
    &lt;li id="9ad3"&gt;Docker and Gitlab CI/CD&lt;/li&gt;
    &lt;li id="d4d6"&gt;Server deployment and Apache and Nginx configuration.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
    &lt;li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="379a"&gt;&lt;strong&gt;January 2019&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="a08a"&gt;In December 2018, however, I built a new PC, which meant I had a place to start from scratch. Although I built it in December, there were some issues with the motherboard so I had to wait until January to get it working.&lt;/p&gt;

&lt;p id="d781"&gt;This time, unlike the previous ones, I knew what I wanted, and I knew how to do it. I set up an LVM with two LVs — one for root and one for home and installed Arch Linux.&lt;/p&gt;

&lt;p id="ea75"&gt;The installation was not so smooth because my WiFi adapter needs a driver that doesn’t ship with the kernel by default. Thankfully it’s available in AUR.&lt;/p&gt;

&lt;p id="7f6d"&gt;I also switched to Fish shell. It’s not POSIX compliant so few of my favourite features don’t work (&amp;amp;&amp;amp; and || for example, although they added them to fish a few weeks after I started using it. What a time to be alive), but change is a good thing.&lt;/p&gt;

&lt;p id="035a"&gt;I also have only Plasma on the desktop and not every single Window manager in existence like the laptop. I also have a strict no-experimentation rule with the work machine.&lt;/p&gt;

&lt;p id="e670"&gt;One good thing I did was customizing Vim. I took inspiration from &lt;a href="https://github.com/amix/vimrc" rel="noopener noreferrer"&gt;here&lt;/a&gt; and changed a few things — replaced Pathogen with Vundle, changed some old plugins to better ones, added a few more, changed some configurations, and replaced Vim with Neovim. As an end result, my Vim is nowhere short of an IDE. Epic Linux guy moment!&lt;/p&gt;

&lt;h2 id="a916"&gt;&lt;strong&gt;April 2019&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="23a0"&gt;I recently came to know about &lt;a href="http://nixos.org" rel="noopener noreferrer"&gt;NixOS&lt;/a&gt;. It has a unique approach to package management. It’s kinda like Yast, but better. The idea is, you write all your packages and configuration in a central location and the OS will build itself according to it. Upgrades are atomic, you can rollback any time. And packages are sandboxed so you can install multiple versions of the same package without conflict, or install packages without worrying about dependencies.&lt;/p&gt;

&lt;p id="e1ce"&gt;So I said bye to the Arch on my laptop and installed NixOS. So far, it’s been a little unconventional to work with, but hopefully I’ll adapt to it soon.&lt;/p&gt;




&lt;h2 id="9580"&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/h2&gt;

&lt;p id="6e1f"&gt;People will say, “Linux is shitty.”, “it does not have many softwares,” “I used to have this on Windows and it’s not on Linux.” etc.
Believe me, I felt the same for the first two months, but I didn’t lose patience, cause I’m switching OS, so I &lt;strong&gt;must&lt;/strong&gt; expect things not to be the same. I’ve been to the dark sides of Linux, and I have seen what it can do.

&lt;/p&gt;
&lt;p id="4035"&gt;I have leaened a &lt;strong&gt;lot&lt;/strong&gt; of things. I have gained a lot of experience. It was hard to learn all by myself without any guide, but if I can do it, anyone can.&lt;/p&gt;

&lt;p id="d71f"&gt;&lt;em&gt;I will update this story with new milestones.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>guide</category>
      <category>experience</category>
    </item>
  </channel>
</rss>
