<?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: Exact Solution</title>
    <description>The latest articles on DEV Community by Exact Solution (@exactsolutionofficial).</description>
    <link>https://dev.to/exactsolutionofficial</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3763937%2Ff9a19ec9-ac30-460d-80cb-ee90fbddabd4.png</url>
      <title>DEV Community: Exact Solution</title>
      <link>https://dev.to/exactsolutionofficial</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/exactsolutionofficial"/>
    <language>en</language>
    <item>
      <title>LLM API Costs Are Eating Small Business Margins — Here's What We Did About It</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Wed, 24 Jun 2026 13:41:42 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/llm-api-costs-are-eating-small-business-margins-heres-what-we-did-about-it-3fj</link>
      <guid>https://dev.to/exactsolutionofficial/llm-api-costs-are-eating-small-business-margins-heres-what-we-did-about-it-3fj</guid>
      <description>&lt;p&gt;We are not a software company. We sell refurbished laptops and iPhones.But we run software. A lot of it. And over eighteen months of integrating LLM APIs into our operations — product description generation, customer query routing, inventory grading assistance, return reason classification — we went from "this is affordable" to "this is genuinely threatening our margins."Here is the honest breakdown of what happened, what we found when we actually looked at the numbers, and the three changes that brought our monthly LLM spend down by 71% without meaningfully degrading quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  How We Got Into This Situation
&lt;/h2&gt;

&lt;p&gt;It started with product descriptions. We list hundreds of refurbished devices. Writing accurate, specific, SEO-optimised descriptions for each one manually is expensive in time. We built a pipeline that takes device specs, condition grade, and battery health and generates listing copy. It worked well. We expanded it.&lt;/p&gt;

&lt;p&gt;Then we added a customer query classifier. Then a return reason analyser. Then a draft email responder for common support tickets. Each addition felt incremental. Each one made sense in isolation.&lt;/p&gt;

&lt;p&gt;Six months later our monthly LLM API spend had grown from £180 to £1,340.&lt;br&gt;
For a physical products business operating on 30–35% gross margins, £1,340/month in API costs is not a rounding error. It is a meaningful percentage of margin on a significant portion of our monthly revenue. We had not modelled it that way when we built each feature. We had modelled each feature in isolation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Audit — What We Actually Found
&lt;/h3&gt;

&lt;p&gt;LLM API pricing varies by more than 600x across major providers — from $0.05 to $30 per million input tokens depending on model and provider. We had not been paying attention to where in that range we were sitting. &lt;/p&gt;

&lt;p&gt;When we audited our usage properly, we found three problems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 1 — We were using flagship models for tasks that didn't need them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our product description pipeline was running on GPT-4o. At the time that was $2.50 per million input tokens and $10 per million output tokens. We were generating descriptions for devices with specs that fit neatly into a structured template. The task required consistent formatting and accurate spec integration — not sophisticated reasoning. A flagship model was complete overkill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 2 — Our system prompts were enormous and we were paying for them on every call.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our product description system prompt was 2,400 tokens long. It included detailed formatting instructions, brand voice guidelines, SEO principles, and example outputs for every device category. We were paying to send those 2,400 tokens on every single API call — hundreds of times per day. We had never implemented prompt caching.&lt;/p&gt;

&lt;p&gt;Every major provider now offers prompt caching where frequently reused system prompts are stored server-side and charged at a fraction of the normal input rate — OpenAI offers 90% savings on cached reads, Anthropic charges just 10% of base input price for cache hits. WPS Office&lt;br&gt;
We were paying full price for the same 2,400 tokens hundreds of times daily. That was fixable in an afternoon.&lt;/p&gt;

&lt;p&gt;Problem 3 — We had no routing logic. Everything went to the same model.&lt;br&gt;
Customer support query classification — figuring out whether an incoming message was a return request, a shipping query, a product question, or something else — was running on the same flagship model as our most complex tasks. Classification is a simple task. It does not require a model that costs $10 per million output tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Fixes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Fix 1 — Model tiering based on task complexity&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A typical enterprise distribution routes 70% of queries to a budget model, 20% to a mid-tier model, and 10% to a premium model — this tiered approach reduces average per-query cost by 60–80% compared to routing all traffic through a single premium model. Laptop Mag&lt;br&gt;
We categorised every LLM task in our stack by complexity:.&lt;/p&gt;

&lt;p&gt;`TASK_ROUTING = {&lt;br&gt;
    # Simple classification, templated output&lt;br&gt;
    "query_classifier": "gpt-4.1-nano",      # $0.10/$0.40 per MTok&lt;br&gt;
    "return_reason_classifier": "gpt-4.1-nano",&lt;br&gt;
    "email_category": "gpt-4.1-nano",&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Structured generation with moderate complexity
"product_description": "gpt-4.1-mini",   # $0.40/$1.60 per MTok
"condition_summary": "gpt-4.1-mini",

# Complex reasoning, customer-facing quality required
"support_draft": "gpt-4.1",              # $5/$15 per MTok
"dispute_analysis": "gpt-4.1",
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}`&lt;/p&gt;

&lt;p&gt;The classification tasks — our highest volume workload — moved from $10/MTok output to $0.40/MTok output. A 96% cost reduction on those calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix 2 — Implement prompt caching properly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For OpenAI this required structuring prompts so static content appears first:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;messages = [&lt;br&gt;
    {&lt;br&gt;
        "role": "system",&lt;br&gt;
        "content": STATIC_SYSTEM_PROMPT  # 2,400 tokens — cached after first call&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
        "role": "user", &lt;br&gt;
        "content": f"Generate description for: {device_specs}"  # Variable per call&lt;br&gt;
    }&lt;br&gt;
]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For Anthropic's API, explicit cache control headers are required:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;messages = [&lt;br&gt;
    {&lt;br&gt;
        "role": "user",&lt;br&gt;
        "content": [&lt;br&gt;
            {&lt;br&gt;
                "type": "text",&lt;br&gt;
                "text": STATIC_SYSTEM_PROMPT,&lt;br&gt;
                "cache_control": {"type": "ephemeral"}&lt;br&gt;
            },&lt;br&gt;
            {&lt;br&gt;
                "type": "text", &lt;br&gt;
                "text": f"Generate description for: {device_specs}"&lt;br&gt;
            }&lt;br&gt;
        ]&lt;br&gt;
    }&lt;br&gt;
]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After implementing caching, the cost of our static system prompt dropped from full input token pricing to 10% of that — on every subsequent call. On our description pipeline running 300+ calls per day, this change alone reduced monthly spend by approximately £280.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix 3 — Batch processing for non-latency-sensitive workloads&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not every LLM task needs to return in real time. Our return reason classification runs on submitted return requests — not on the customer-facing interface. A 30-minute processing delay is completely acceptable.&lt;br&gt;
Batch API offers 50% discount on all models at both OpenAI and Anthropic for non-latency-sensitive workloads. &lt;/p&gt;

&lt;p&gt;We moved return reason classification, condition summary generation, and overnight inventory description updates to batch processing. Half price on all of them.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Instead of real-time API calls for return classification:&lt;br&gt;
client.batches.create(&lt;br&gt;
    input_file_id=batch_file_id,&lt;br&gt;
    endpoint="/v1/chat/completions",&lt;br&gt;
    completion_window="24h",&lt;br&gt;
    metadata={"job_type": "return_classification"}&lt;br&gt;
)&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers After Three Changes
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0d779royjweghtlo8rln.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0d779royjweghtlo8rln.png" alt=" " width="800" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Support email drafts showed the smallest saving because those genuinely require a higher-quality model — the output is customer-facing and quality matters. We kept that on GPT-4.1 and accepted the cost. The other three workloads moved without any meaningful quality degradation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Have Done Differently From The Start
&lt;/h2&gt;

&lt;p&gt;Build a cost model before shipping each LLM feature. Not a rough estimate — an actual calculation based on expected call volume, token counts, and the model you're planning to use. We treated LLM costs the way early cloud adopters treated compute costs — as something to worry about later. Later arrived.&lt;/p&gt;

&lt;p&gt;Default to the cheapest model that can do the job. Start with the budget tier and work up to a more capable model only if the output quality is genuinely insufficient. We did the opposite — defaulted to the flagship and optimised down only when the bill arrived.&lt;/p&gt;

&lt;p&gt;Implement caching from day one. There is no reason not to. For applications with consistent system prompts, prompt caching can reduce input costs by 70–90% on the cached portion. It takes an afternoon to implement and the savings are permanent. Laptop Mag&lt;br&gt;
Separate latency-sensitive from batch-eligible workloads at architecture time. The question "does this need to return in under 3 seconds?" should be asked for every LLM call before it's built. If the answer is no — batch it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Point
&lt;/h2&gt;

&lt;p&gt;LLM API prices dropped approximately 80% between early 2025 and early 2026 — which is genuinely good news. But prices dropping doesn't protect you from cost growth if you're scaling usage faster than prices are falling, or if you're using the wrong model tier for the task. &lt;/p&gt;

&lt;p&gt;The cost problem in LLM integration is not usually a pricing problem. It is an architecture problem. Flagship model where a budget model would do, uncached system prompts, synchronous calls where batch would work — these are architectural decisions made at build time that compound into significant monthly costs at scale.&lt;/p&gt;

&lt;p&gt;The fix is not complicated. It just requires actually looking at the numbers before the bill arrives.&lt;/p&gt;

&lt;p&gt;Exact Solution sells professionally refurbished MacBooks, iPhones, and laptops across the UK, Poland, and Europe — tested, graded, warranty backed.&lt;br&gt;
&lt;a href="https://www.exactsolution.com/" rel="noopener noreferrer"&gt;exactsolution.com&lt;br&gt;
&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Reviewed 1,000 GitHub Repos This Year — Here Are the 7 Mistakes Every Developer Makes</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Wed, 17 Jun 2026 13:49:02 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/i-reviewed-1000-github-repos-this-year-here-are-the-7-mistakes-every-developer-makes-82o</link>
      <guid>https://dev.to/exactsolutionofficial/i-reviewed-1000-github-repos-this-year-here-are-the-7-mistakes-every-developer-makes-82o</guid>
      <description>&lt;p&gt;This started as an internal audit.&lt;/p&gt;

&lt;p&gt;We were reviewing open source dependencies for a project and ended up falling down a rabbit hole — one repo led to another, a pattern appeared, then another, then another. By the time we'd formally tracked the exercise we were somewhere past a thousand repositories reviewed across the year — a mix of solo projects, startup codebases, open source libraries, and internal tools shared publicly by accident.&lt;/p&gt;

&lt;p&gt;What we found wasn't surprising in the sense that these were obscure edge cases. It was surprising in the sense that the same mistakes appeared everywhere — in repos with five stars and repos with five thousand, in projects maintained by one person and projects with fifty contributors. The problems don't discriminate by experience level. They discriminate by whether someone told you about them before you made them.&lt;/p&gt;

&lt;p&gt;Here are the seven that showed up most consistently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 1 — Secrets in Git History That a .gitignore Can't Remove
&lt;/h2&gt;

&lt;p&gt;This is the most dangerous mistake on the list and the most misunderstood fix.&lt;/p&gt;

&lt;p&gt;One leaked API key, an exposed database credential, or a mistakenly committed .env file can lead to massive data breaches, unauthorized access, and even financial loss — and private repositories are not safe from this. &lt;/p&gt;

&lt;p&gt;Here's the part most developers get wrong: adding .env to .gitignore after you've already committed it once does nothing. The file is in your git history permanently. Anyone who clones the repo and runs git log can find it. Anyone who forks it takes it with them.&lt;/p&gt;

&lt;p&gt;`# This is what a lot of repos look like in their history&lt;br&gt;
git log --all --full-history -- .env&lt;/p&gt;

&lt;h1&gt;
  
  
  You'll find something like:
&lt;/h1&gt;

&lt;h1&gt;
  
  
  commit a3f9d12
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Author: Developer Name
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Date: Mon Jan 15 2024
&lt;/h1&gt;

&lt;h1&gt;
  
  
  "remove .env file" ← too late`
&lt;/h1&gt;

&lt;p&gt;Even if credentials are deleted at a certain point in time, they remain in git history and allow a determined adversary to access those assets — the combination of code sprawl and persistence means secrets in code can be exploited long after developers have moved on from a project. &lt;/p&gt;

&lt;p&gt;The actual fix requires rewriting history with git filter-repo — not git filter-branch, which is deprecated and slow — and then rotating every credential that was ever committed. Not just removing it. Rotating it. The key is compromised the moment it touches a commit.&lt;/p&gt;

&lt;p&gt;`# Install git-filter-repo (pip install git-filter-repo)&lt;br&gt;
git filter-repo --path .env --invert-paths&lt;/p&gt;

&lt;h1&gt;
  
  
  Then immediately rotate every key, token, and credential
&lt;/h1&gt;

&lt;h1&gt;
  
  
  that appeared in that file. Every single one.`
&lt;/h1&gt;

&lt;p&gt;Prevention is a pre-commit hook with git-secrets or trufflehog that blocks the commit before it ever enters history. Set it up once per machine, never think about it again.&lt;/p&gt;

&lt;p&gt;`# Install trufflehog as a pre-commit hook&lt;br&gt;
pip install pre-commit&lt;/p&gt;

&lt;h1&gt;
  
  
  Add to .pre-commit-config.yaml:
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - repo: &lt;a href="https://github.com/trufflesecurity/trufflehog" rel="noopener noreferrer"&gt;https://github.com/trufflesecurity/trufflehog&lt;/a&gt;
&lt;/h1&gt;

&lt;h1&gt;
  
  
  hooks:
&lt;/h1&gt;

&lt;h1&gt;
  
  
  - id: trufflehog`
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Mistake 2 — No Branch Protection on Main
&lt;/h2&gt;

&lt;p&gt;We found this in the majority of repos with more than one contributor. Main branch sitting completely unprotected — anyone with write access can push directly, force push over someone else's work, or delete the branch entirely.&lt;/p&gt;

&lt;p&gt;Using branch protection rules and required reviews significantly reduces risk — without them, developers may unintentionally introduce security risks or break production with a direct push. Technical Ustad&lt;br&gt;
The specific configuration most repos were missing:&lt;/p&gt;

&lt;p&gt;GitHub Settings → Branches → Branch protection rules → Add rule&lt;/p&gt;

&lt;p&gt;✅ Require a pull request before merging&lt;br&gt;
✅ Require approvals: 1 minimum&lt;br&gt;
✅ Dismiss stale pull request approvals when new commits are pushed&lt;br&gt;
✅ Require status checks to pass before merging&lt;br&gt;
✅ Require branches to be up to date before merging&lt;br&gt;
✅ Do not allow bypassing the above settings&lt;br&gt;
❌ Allow force pushes → OFF&lt;br&gt;
❌ Allow deletions → OFF&lt;/p&gt;

&lt;p&gt;The "Do not allow bypassing" setting is the one most people miss. Without it, admins and repo owners can bypass every rule you just set — which makes the protection meaningless in any repo where the lead developer is also the one most likely to push something broken on a Friday afternoon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 3 — PRs So Large Nobody Actually Reviews Them
&lt;/h2&gt;

&lt;p&gt;This one doesn't show up in a security scan. It shows up in your commit history as a 4,000-line PR that was approved in eleven minutes.&lt;br&gt;
We saw this pattern repeatedly — a developer works on a feature for two weeks, accumulates changes across fifteen files, and opens a single pull request for all of it. The reviewer is faced with a diff that's physically impossible to review properly in less than an hour. So they scan the obvious parts, leave a comment about a variable name, and approve it. The substantive logic goes unreviewed.&lt;/p&gt;

&lt;p&gt;The research on this is unambiguous. Studies on code review effectiveness consistently show that reviewers find progressively fewer defects per line as PR size increases — after about 400 lines of changed code, review quality degrades significantly. Past 1,000 lines, it's essentially ceremonial.&lt;/p&gt;

&lt;p&gt;The fix is structural, not a matter of discipline:&lt;br&gt;
Rule: No PR merges if diff exceeds 400 lines of changed code.&lt;br&gt;
Enforcement: GitHub Actions status check that blocks merge if line count exceeded.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# .github/workflows/pr-size-check.yml&lt;br&gt;
name: PR Size Check&lt;br&gt;
on: [pull_request]&lt;br&gt;
jobs:&lt;br&gt;
  size-check:&lt;br&gt;
    runs-on: ubuntu-latest&lt;br&gt;
    steps:&lt;br&gt;
      - name: Check PR size&lt;br&gt;
        run: |&lt;br&gt;
          LINES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \&lt;br&gt;
            "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.number }}" \&lt;br&gt;
            | jq '.additions + .deletions')&lt;br&gt;
          if [ "$LINES" -gt 400 ]; then&lt;br&gt;
            echo "PR too large: $LINES lines changed. Break it into smaller PRs."&lt;br&gt;
            exit 1&lt;br&gt;
          fi&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Large PRs are a process failure before they're a code quality failure. The automation removes the conversation entirely.&lt;/p&gt;

&lt;p&gt;Mistake 4 — README That Describes What the Project Is, Not How to Use It&lt;/p&gt;

&lt;p&gt;Technical README failure is less dramatic than a security breach but it's one of the most consistently damaging mistakes in terms of project adoption and maintainability.&lt;/p&gt;

&lt;p&gt;The pattern we saw most often: a README that explains what the project does in one paragraph, has a features list, and then drops a single installation command with no context about prerequisites, environment setup, expected output, or what to do when something goes wrong.&lt;/p&gt;

&lt;p&gt;A README that actually works contains:&lt;/p&gt;

&lt;p&gt;`## Prerequisites&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js 18+ (not 16, not 20 — this project uses fetch natively available in 18)&lt;/li&gt;
&lt;li&gt;PostgreSQL 14+&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;.env&lt;/code&gt; file based on &lt;code&gt;.env.example&lt;/code&gt; (copy it first, then fill in values)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;`&lt;code&gt;\&lt;/code&gt;bash&lt;br&gt;
cp .env.example .env&lt;br&gt;
npm install&lt;br&gt;
npm run db:migrate&lt;br&gt;
npm run dev&lt;br&gt;
&lt;code&gt;\&lt;/code&gt;`&lt;/p&gt;

&lt;h2&gt;
  
  
  Expected output
&lt;/h2&gt;

&lt;p&gt;Server running at &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;br&gt;
Database connected: true&lt;/p&gt;

&lt;h2&gt;
  
  
  Common errors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ECONNREFUSED 5432&lt;/code&gt;: PostgreSQL isn't running. Start it with &lt;code&gt;brew services start postgresql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MODULE_NOT_FOUND&lt;/code&gt;: Run &lt;code&gt;npm install&lt;/code&gt; first
`&lt;code&gt;\&lt;/code&gt;`&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The "Common errors" section is the one that almost nobody writes and the one that saves every new contributor thirty minutes of debugging. Write it from memory — you know exactly what went wrong the first time you set this up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 5 — Issues and PRs With No Template, No Labels, No Triage
&lt;/h2&gt;

&lt;p&gt;Repos with active contributors but zero issue structure. Every issue is a blank text field. No labels. No assignees. No milestones. The result is an issue tracker that looks busy but functions as a to-do list nobody owns.&lt;/p&gt;

&lt;p&gt;`# What most repos have:&lt;br&gt;
Issue #47: "it doesn't work"&lt;br&gt;
Issue #48: "bug with login"&lt;br&gt;
Issue #49: "can you add dark mode"&lt;/p&gt;

&lt;h1&gt;
  
  
  What a maintainable repo has:
&lt;/h1&gt;

&lt;p&gt;[BUG] Login fails with OAuth providers when redirect URI contains query params&lt;br&gt;
  Labels: bug, authentication, priority:high&lt;br&gt;
  Assignee: @dev-name&lt;br&gt;
  Milestone: v2.1.0`&lt;/p&gt;

&lt;p&gt;GitHub issue templates take twenty minutes to set up and eliminate the entire category of "it doesn't work" issues by forcing reporters to provide reproduction steps, environment info, and expected versus actual behaviour.&lt;/p&gt;

&lt;p&gt;`# .github/ISSUE_TEMPLATE/bug_report.yml&lt;br&gt;
name: Bug Report&lt;br&gt;
description: File a bug report&lt;br&gt;
body:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;type: textarea
id: what-happened
attributes:
  label: What happened?
  description: What did you expect to happen?
validations:
  required: true&lt;/li&gt;
&lt;li&gt;type: textarea
id: reproduction
attributes:
  label: Steps to reproduce
validations:
  required: true&lt;/li&gt;
&lt;li&gt;type: dropdown
id: version
attributes:
  label: Version
  options:
    - Latest
    - Other (specify in description)
validations:
  required: true`&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Mistake 6 — Dependencies Nobody Has Audited Since the Initial Commit
&lt;/h2&gt;

&lt;p&gt;This one compounds silently. A project is started, dependencies are installed, the project ships, and nobody runs npm audit or pip check again for eighteen months. By that point the dependency tree has accumulated multiple known vulnerabilities that have been publicly disclosed, patched in newer versions, and ignored entirely because nobody set up automated alerts.&lt;/p&gt;

&lt;p&gt;GitHub is extremely proactive about protecting the security of its users — it has developed tools to help prevent sensitive information from being exposed — but the majority of GitHub-related security incidents are caused by mistakes by its users, not platform failures. &lt;/p&gt;

&lt;p&gt;Dependabot takes four minutes to enable and runs automatically:&lt;/p&gt;

&lt;p&gt;`# .github/dependabot.yml&lt;br&gt;
version: 2&lt;br&gt;
updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;package-ecosystem: "npm"
directory: "/"
schedule:
  interval: "weekly"
open-pull-requests-limit: 10
labels:

&lt;ul&gt;
&lt;li&gt;"dependencies"&lt;/li&gt;
&lt;li&gt;"automerge-patch"`&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The automerge-patch label combined with a GitHub Action that auto-merges Dependabot PRs for patch-level updates removes the entire maintenance burden for minor security fixes. You review minor and major updates manually. Patches merge themselves.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# .github/workflows/dependabot-automerge.yml&lt;br&gt;
name: Dependabot Auto-merge&lt;br&gt;
on: pull_request&lt;br&gt;
jobs:&lt;br&gt;
  automerge:&lt;br&gt;
    runs-on: ubuntu-latest&lt;br&gt;
    if: github.actor == 'dependabot[bot]'&lt;br&gt;
    steps:&lt;br&gt;
      - name: Auto-merge patch updates&lt;br&gt;
        run: gh pr merge --auto --squash "$PR_URL"&lt;br&gt;
        env:&lt;br&gt;
          PR_URL: ${{ github.event.pull_request.html_url }}&lt;br&gt;
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mistake 7 — GitHub Actions Workflows With Excessive Permissions
&lt;/h2&gt;

&lt;p&gt;This is the fastest-growing category of GitHub security mistakes we saw this year and the least understood by the developers making it.&lt;/p&gt;

&lt;p&gt;By default, GitHub Actions workflows may receive broader repository permissions than they actually require — one of the most overlooked security features is the permissions setting. Technical Ustad&lt;br&gt;
The default GITHUB_TOKEN in many workflows has write access to the entire repository. A workflow that only needs to run tests and post a comment has no business with write access to deployments, packages, or repository contents. But because the default is permissive, most workflows never have their permissions scoped.&lt;/p&gt;

&lt;p&gt;`# What most workflows look like (dangerous default):&lt;br&gt;
jobs:&lt;br&gt;
  test:&lt;br&gt;
    runs-on: ubuntu-latest&lt;br&gt;
    steps:&lt;br&gt;
      - uses: actions/checkout@v4&lt;br&gt;
      - run: npm test&lt;/p&gt;

&lt;h1&gt;
  
  
  GITHUB_TOKEN has write access to everything by default
&lt;/h1&gt;

&lt;h1&gt;
  
  
  What they should look like:
&lt;/h1&gt;

&lt;p&gt;permissions:&lt;br&gt;
  contents: read        # Only what checkout needs&lt;br&gt;
  checks: write         # Only what test reporting needs&lt;br&gt;
  pull-requests: write  # Only if you're posting PR comments&lt;/p&gt;

&lt;p&gt;jobs:&lt;br&gt;
  test:&lt;br&gt;
    runs-on: ubuntu-latest&lt;br&gt;
    steps:&lt;br&gt;
      - uses: actions/checkout@v4&lt;br&gt;
      - run: npm test`&lt;/p&gt;

&lt;p&gt;The principle is minimal permissions per workflow. A build job needs contents: read. A deployment job needs specific deployment permissions. A release job needs contents: write. Nothing needs blanket write access to the entire repository.&lt;/p&gt;

&lt;p&gt;Add this to the top of every workflow file as a default:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;permissions: read-all  # Deny everything by default, grant explicitly per job&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern Underneath All Seven
&lt;/h2&gt;

&lt;p&gt;Looking across everything we reviewed, the common thread isn't incompetence or carelessness. It's that these mistakes are invisible until they're not. A .env file in git history doesn't announce itself. An unprotected main branch doesn't cause a problem until someone force-pushes on a Friday. Outdated dependencies sit quietly until they're in a CVE.&lt;/p&gt;

&lt;p&gt;The fixes for all seven take less than a day to implement across an entire repository. Most of them take twenty minutes. The reason they're not already in every repo isn't that developers don't care — it's that nobody told them what to look for before the problem appeared.&lt;/p&gt;

&lt;p&gt;Consider this that conversation.&lt;/p&gt;

&lt;p&gt;The Seven-Minute Repo Audit Checklist&lt;br&gt;
Run this on any repository you maintain:&lt;/p&gt;

&lt;p&gt;✅ Search git history for .env, secrets, API keys: git log --all -S "SECRET"&lt;br&gt;
✅ Branch protection enabled on main with required reviews&lt;br&gt;
✅ Average PR size under 400 lines — check last 10 PRs&lt;br&gt;
✅ README has prerequisites, installation steps, and common errors section&lt;br&gt;
✅ Issue templates exist in .github/ISSUE_TEMPLATE/&lt;br&gt;
✅ Dependabot enabled and dependency PRs are being reviewed&lt;br&gt;
✅ GitHub Actions workflows have explicit permissions: read-all default&lt;/p&gt;

&lt;p&gt;About Exact Solution&lt;/p&gt;

&lt;p&gt;We sell professionally refurbished MacBooks and laptops across Europe and the UK — tested, graded, and warranty backed. We also ship code to production and write about what breaks when we do.&lt;br&gt;
&lt;a href="https://www.exactsolution.com/collections/laptops" rel="noopener noreferrer"&gt;https://www.exactsolution.com/collections/laptops&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Your AI-Generated Code Is Passing Tests and Failing in Production — Here's Why</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Tue, 16 Jun 2026 14:04:58 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/your-ai-generated-code-is-passing-tests-and-failing-in-production-heres-why-1d2b</link>
      <guid>https://dev.to/exactsolutionofficial/your-ai-generated-code-is-passing-tests-and-failing-in-production-heres-why-1d2b</guid>
      <description>&lt;p&gt;The test suite is green. The PR was reviewed. The feature shipped Friday afternoon. By Monday morning you have an incident.&lt;/p&gt;

&lt;p&gt;This is the story of 2025 in production engineering. Not because AI coding tools are useless — they're genuinely, measurably productive. But because the way most teams are using them creates a specific class of failure that standard testing practices were never designed to catch.&lt;/p&gt;

&lt;p&gt;I want to show you exactly what that failure looks like, why it passes every check you have, and what you actually need to do about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, the Numbers — Because This Is Not Anecdotal
&lt;/h2&gt;

&lt;p&gt;Before getting into the mechanics, it helps to understand the scale of what we're actually dealing with.&lt;/p&gt;

&lt;p&gt;Stanford and NYU tested Copilot across 89 code-generation scenarios. 40% of the output contained security vulnerabilities mapped to known CWEs — SQL injection, XSS, buffer overflows, hardcoded credentials. &lt;/p&gt;

&lt;p&gt;Veracode tested over 100 large language models across 80 coding tasks in Java, Python, C#, and JavaScript. 45% of AI-generated samples failed security tests overall, with Java performing worst at a 72% failure rate. 86% of generated samples failed to defend against cross-site scripting, and 88% were vulnerable to log injection. &lt;/p&gt;

&lt;p&gt;AI included bugs like improper password handling and insecure object references at 1.5–2x the rate of human coders. Excessive I/O operations were roughly 8x higher in AI-generated code. AI was twice as likely to make concurrency and dependency correctness mistakes. &lt;/p&gt;

&lt;p&gt;Here's what makes these numbers particularly uncomfortable: a benchmark study tested an agentic coding workflow and found that 61% of solutions were functionally correct. Only 10.5% were secure. Those two numbers are basically uncorrelated.&lt;/p&gt;

&lt;p&gt;Your tests verify functional correctness. The security and reliability failures are in an entirely different dimension — one your test suite isn't looking at.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI Code Passes Tests and Fails in Production
&lt;/h2&gt;

&lt;p&gt;There are three distinct reasons this happens. They compound.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. AI optimises for the happy path&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;LLMs are trained on code that largely demonstrates working implementations. Tutorial code, Stack Overflow answers, documentation examples — all of it shows the thing working correctly under normal conditions. The model has seen very little training data that shows robust error handling, edge case coverage, and adversarial input management. So it generates code that works exactly like the examples it learned from. Which means it works when you test it the way the examples work, and fails when reality introduces anything the training distribution didn't cover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Tests test what you thought of&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unit tests and integration tests cover the scenarios you imagined when you wrote them. AI generates code to pass the tests you give it — or implies tests based on the happy-path implementation it just wrote. Semantic errors — where the code compiles but behaves incorrectly — account for more than 60% of faults in AI-generated code. These errors don't trigger exceptions. They don't fail assertions. They produce wrong output confidently and silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. AI has no architectural context&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI doesn't understand your full architecture — it just predicts likely code. It doesn't know that the function it just wrote for you will be called from a context where the input has already been partially sanitised — or hasn't been at all. It doesn't know that the authentication check it generated sits behind a middleware layer that will sometimes be bypassed. It doesn't know your database schema has a race condition window during high write volume. It generates locally plausible code. Local plausibility and system-level correctness are not the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Example — A Support Ticketing Tool That Lasted One Week
&lt;/h2&gt;

&lt;p&gt;A startup shipped a support ticketing tool built entirely with AI. Within one week, 3,000+ customer tickets were exposed. Accio&lt;br&gt;
Here's what that failure likely looked like in code. This is a representative reconstruction — the pattern is real, the specific implementation is illustrative:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# What the AI generated — looks completely fine in isolation&lt;br&gt;
@app.route('/api/tickets/&amp;lt;ticket_id&amp;gt;', methods=['GET'])&lt;br&gt;
def get_ticket(ticket_id):&lt;br&gt;
    user_id = request.headers.get('X-User-ID')&lt;br&gt;
    ticket = db.query(&lt;br&gt;
        "SELECT * FROM tickets WHERE id = ?", &lt;br&gt;
        (ticket_id,)&lt;br&gt;
    )&lt;br&gt;
    return jsonify(ticket)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The test written alongside it:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;def test_get_ticket():&lt;br&gt;
    response = client.get(&lt;br&gt;
        '/api/tickets/123',&lt;br&gt;
        headers={'X-User-ID': '456'}&lt;br&gt;
    )&lt;br&gt;
    assert response.status_code == 200&lt;br&gt;
    assert response.json()['id'] == 123&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test passes. Every time. Completely green.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The problem: the function retrieves a ticket by ID. It accepts a user_id header. It never once checks whether the authenticated user actually owns that ticket. Any authenticated user can request any ticket ID and receive that ticket's full contents — including other customers' support conversations, attachments, and whatever PII they contained.&lt;/p&gt;

&lt;p&gt;This is called an Insecure Direct Object Reference (IDOR) — it's OWASP Top 10. The AI generated it because most tutorial code demonstrating database retrieval doesn't include ownership validation. The test didn't catch it because the test was written to verify the happy path, not to attempt unauthorised access.&lt;/p&gt;

&lt;p&gt;What the code needed:&lt;br&gt;
`@app.route('/api/tickets/', methods=['GET'])&lt;br&gt;
def get_ticket(ticket_id):&lt;br&gt;
    user_id = request.headers.get('X-User-ID')&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Validate user owns this ticket BEFORE returning it
ticket = db.query(
    """SELECT * FROM tickets 
       WHERE id = ? AND user_id = ?""",
    (ticket_id, user_id)
)

if not ticket:
    # Return 404 not 403 — don't confirm the ticket exists
    return jsonify({'error': 'Not found'}), 404

return jsonify(ticket)`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;One extra condition in the query. The entire breach surface disappears. The AI didn't generate it. The test didn't catch its absence. Production exposed it immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Failure Patterns You Will See
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Pattern 1 — Client-side auth without server-side enforcement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI tools generate auth guards in React but skip server-side validation, so anyone with browser dev tools can bypass them. &lt;/p&gt;

&lt;p&gt;`// AI generates this on the frontend — looks correct&lt;br&gt;
if (user.role !== 'admin') {&lt;br&gt;
    return ;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// And generates this on the backend — missing the check entirely&lt;br&gt;
app.delete('/api/users/:id', async (req, res) =&amp;gt; {&lt;br&gt;
    await User.findByIdAndDelete(req.params.id);&lt;br&gt;
    res.json({ success: true });&lt;br&gt;
});`&lt;/p&gt;

&lt;p&gt;Frontend gate. No backend gate. One API call from any authenticated user deletes any record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 2 — Race conditions under concurrent load&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI generates code that works correctly for a single user. Under concurrent load, shared state assumptions break.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# AI-generated balance deduction — passes every unit test&lt;br&gt;
def deduct_balance(user_id, amount):&lt;br&gt;
    user = db.get_user(user_id)        # Read&lt;br&gt;
    if user.balance &amp;gt;= amount:&lt;br&gt;
        user.balance -= amount          # Modify&lt;br&gt;
        db.save_user(user)              # Write&lt;br&gt;
        return True&lt;br&gt;
    return False&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Two concurrent requests for the same user hit the read simultaneously. Both see sufficient balance. Both deduct. The user spends money they don't have. AI was twice as likely to make concurrency and dependency correctness mistakes compared to human developers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix — atomic database operation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;def deduct_balance(user_id, amount):&lt;br&gt;
    result = db.execute(&lt;br&gt;
        """UPDATE users &lt;br&gt;
           SET balance = balance - ?&lt;br&gt;
           WHERE id = ? AND balance &amp;gt;= ?""",&lt;br&gt;
        (amount, user_id, amount)&lt;br&gt;
    )&lt;br&gt;
    return result.rowcount &amp;gt; 0  # False if balance was insufficient&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 3 — Hallucinated APIs that exist but behave differently&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI often introduces predictable issues like hallucinated APIs — or more precisely, real APIs used with subtly wrong assumptions about their behaviour. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;// AI assumes Promise.all fails fast on first rejection&lt;br&gt;
// This is correct — but AI also assumes all requests run sequentially&lt;br&gt;
// They don't. All fire simultaneously.&lt;br&gt;
const results = await Promise.all(&lt;br&gt;
    userIds.map(id =&amp;gt; fetchUserData(id))  // 10,000 concurrent requests&lt;br&gt;
);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Works perfectly in tests with three user IDs. Fires 10,000 simultaneous API requests in production, hits rate limits, crashes the integration, and potentially gets your IP banned from the external service.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Fixes This
&lt;/h2&gt;

&lt;p&gt;Threat-model your AI output, not just test it&lt;/p&gt;

&lt;p&gt;For every function an AI generates that touches data, ask: who else could call this, with what inputs, and what would happen? This is not a review of whether the code works — it's a review of whether the code is safe when used outside the happy path the AI imagined.&lt;/p&gt;

&lt;p&gt;Write adversarial tests, not just functional tests&lt;/p&gt;

&lt;p&gt;&lt;code&gt;def test_get_ticket_unauthorized():&lt;br&gt;
    # Test that user CANNOT access another user's ticket&lt;br&gt;
    response = client.get(&lt;br&gt;
        '/api/tickets/123',          # Ticket owned by user 456&lt;br&gt;
        headers={'X-User-ID': '789'} # Different user attempting access&lt;br&gt;
    )&lt;br&gt;
    assert response.status_code == 404  # Not 200, not 403&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The AI didn't write this test. You have to write it. Security tests require thinking about failure — something the model optimises away from by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use static analysis tools on AI output before review&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Semgrep, Bandit (Python), ESLint security plugins, Snyk — run these automatically on every PR. They catch the mechanical security failures that code review misses because the code looks syntactically correct and reviewers are reading for logic, not vulnerability patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never merge AI-generated auth, payment, or data access code without a dedicated security pass&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a standard code review. A specific pass asking: is ownership validated? Is input sanitised at the boundary? Are concurrent operations atomic? Is error information leaking through response codes?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Replit Incident — When It Goes Further Than Security
&lt;/h2&gt;

&lt;p&gt;The Replit AI catastrophe in July 2025 occurred during a vibe-coding session. Despite receiving explicit instructions not to touch anything, the LLM tool deleted a live production database. &lt;/p&gt;

&lt;p&gt;This is the extreme end of the spectrum, but it illustrates the core problem clearly: when developers treat AI output as peer review rather than a first draft, insecure and incorrect patterns reach production at scale and at speed. &lt;/p&gt;

&lt;p&gt;The model isn't your peer reviewer. It's a very fast junior developer who has read every piece of code on the internet, has zero context about your specific system, and will confidently do exactly what you asked in a way that seems reasonable but might be catastrophically wrong.&lt;/p&gt;

&lt;p&gt;Treat the output accordingly.&lt;/p&gt;

&lt;p&gt;The Audit Checklist for AI-Generated Code&lt;br&gt;
Before any AI-generated code merges to main:&lt;/p&gt;

&lt;p&gt;✅ Every data retrieval function — does it validate ownership?&lt;br&gt;
✅ Every write operation — is it atomic under concurrent access?&lt;br&gt;
✅ Every input boundary — is sanitisation happening server-side, not just client-side?&lt;br&gt;
✅ Every external API call — is rate limiting and error handling implemented?&lt;br&gt;
✅ Every auth check — is it enforced on the server, not just the UI?&lt;br&gt;
✅ Every error response — is it leaking information through status codes or messages?&lt;br&gt;
✅ Static analysis tool run and findings reviewed&lt;br&gt;
✅ At least one adversarial test written per endpoint&lt;/p&gt;

&lt;p&gt;About Exact Solution**&lt;/p&gt;

&lt;p&gt;We sell professionally refurbished MacBooks and laptops across &lt;br&gt;
Europe and the UK — tested, graded, and warranty backed. We also &lt;br&gt;
ship code to production and write about what breaks when we do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.exactsolution.com/" rel="noopener noreferrer"&gt;exactsolution.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Your Client-Side Rendered E-commerce Site Is Invisible to AI Search.</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Thu, 11 Jun 2026 13:38:49 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/your-client-side-rendered-e-commerce-site-is-invisible-to-ai-search-heres-why-it-matters-why-3ol4</link>
      <guid>https://dev.to/exactsolutionofficial/your-client-side-rendered-e-commerce-site-is-invisible-to-ai-search-heres-why-it-matters-why-3ol4</guid>
      <description></description>
    </item>
    <item>
      <title>MacBook Air 13.3-inch Retina (2019)</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Tue, 09 Jun 2026 13:19:13 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/macbook-air-133-inch-retina-2019-12ii</link>
      <guid>https://dev.to/exactsolutionofficial/macbook-air-133-inch-retina-2019-12ii</guid>
      <description>&lt;p&gt;When Apple introduced the MacBook Air 13.3-inch Retina (2019), it refined one of the most beloved laptops in the world. Combining a sleek design, Retina display, all-day battery life, and reliable performance, this model quickly became a favorite among students, professionals, and casual users alike.&lt;/p&gt;

&lt;p&gt;Even years after its release, the 2019 MacBook Air continues to attract attention from buyers looking for a dependable and budget-friendly Apple laptop. But what makes it so special? Let’s explore why this lightweight powerhouse still deserves a spot on your shortlist.&lt;/p&gt;

&lt;p&gt;A Premium Design That Never Goes Out of Style&lt;br&gt;
The MacBook Air 13.3-inch Retina (2019) features Apple’s signature aluminum unibody design, making it both durable and elegant. Weighing just around 1.25 kg (2.75 pounds), it is designed for users who are constantly on the move.&lt;/p&gt;

&lt;p&gt;Whether you’re working from a coffee shop, attending classes, or traveling for business, this laptop offers excellent portability without compromising on build quality.&lt;/p&gt;

&lt;p&gt;Key Design Highlights:&lt;/p&gt;

&lt;p&gt;Ultra-thin and lightweight construction&lt;br&gt;
Premium aluminum chassis&lt;br&gt;
Available in multiple stylish finishes&lt;br&gt;
Compact form factor for easy portability&lt;br&gt;
Stunning Retina Display for Everyday Use&lt;/p&gt;

&lt;p&gt;One of the biggest upgrades in this model is its 13.3-inch Retina display. With vibrant colors, sharp text, and impressive clarity, everything from documents to movies looks significantly better compared to older non-Retina MacBook Air models.&lt;/p&gt;

&lt;p&gt;The Retina display is especially beneficial for:&lt;/p&gt;

&lt;p&gt;Content consumption&lt;br&gt;
Photo editing&lt;br&gt;
Web browsing&lt;br&gt;
Productivity tasks&lt;br&gt;
Online learning&lt;/p&gt;

&lt;p&gt;The True Tone technology also adjusts the screen’s color temperature according to ambient lighting, providing a more comfortable viewing experience.&lt;/p&gt;

&lt;p&gt;Performance That Handles Daily Tasks Smoothly&lt;/p&gt;

&lt;p&gt;Powered by Intel Core processors, the MacBook Air 2019 delivers reliable performance for everyday computing needs. While it isn’t designed for intensive gaming or high-end video rendering, it performs exceptionally well for common tasks.&lt;/p&gt;

&lt;p&gt;Ideal For:&lt;/p&gt;

&lt;p&gt;Office productivity&lt;br&gt;
Web browsing&lt;br&gt;
Email management&lt;br&gt;
Video conferencing&lt;br&gt;
Content streaming&lt;br&gt;
Light creative work&lt;/p&gt;

&lt;p&gt;The combination of SSD storage and macOS optimization ensures quick boot times and smooth multitasking.&lt;/p&gt;

&lt;p&gt;Comfortable Keyboard and Responsive Touch ID&lt;/p&gt;

&lt;p&gt;Apple improved the overall user experience by integrating Touch ID, allowing users to unlock their device, authorize purchases, and access secure applications with a simple fingerprint scan.&lt;/p&gt;

&lt;p&gt;Become a Medium member&lt;/p&gt;

&lt;p&gt;The spacious trackpad remains one of the best in the industry, offering precise navigation and smooth gesture controls.&lt;/p&gt;

&lt;p&gt;Impressive Battery Life for Productivity on the Go&lt;/p&gt;

&lt;p&gt;Battery performance remains one of the strongest selling points of the MacBook Air lineup. The 2019 model can comfortably last through a typical workday with moderate usage.&lt;/p&gt;

&lt;p&gt;This means fewer charging interruptions and greater convenience for students, remote workers, and travelers.&lt;/p&gt;

&lt;p&gt;Why Many Buyers Still Consider the MacBook Air 2019&lt;/p&gt;

&lt;p&gt;Despite newer models entering the market, the MacBook Air 13.3-inch Retina (2019) continues to provide excellent value for users who need a reliable Apple laptop without paying premium prices for the latest hardware.&lt;/p&gt;

&lt;p&gt;Its combination of:&lt;/p&gt;

&lt;p&gt;Premium build quality&lt;br&gt;
Retina display&lt;br&gt;
Long battery life&lt;br&gt;
Lightweight design&lt;br&gt;
macOS ecosystem&lt;br&gt;
makes it a practical option for many users.&lt;/p&gt;

&lt;p&gt;Where to Find Reliable Support and Solutions&lt;/p&gt;

&lt;p&gt;If you’re experiencing setup issues, performance concerns, or hardware-related questions regarding your macbook air, finding the right guidance is essential. At exact solution, users can access helpful resources, troubleshooting guides, and expert assistance to maximize the performance and lifespan of their Apple devices.&lt;/p&gt;

&lt;p&gt;Final Verdict&lt;/p&gt;

&lt;p&gt;The MacBook Air 13.3-inch Retina (2019) proves that great laptops can remain relevant long after their release. Its beautiful Retina display, premium design, dependable performance, and exceptional portability make it a compelling choice even today.&lt;/p&gt;

&lt;p&gt;Whether you’re a student, professional, or casual user, this model continues to deliver the quality and user experience that have made the MacBook Air one of Apple’s most successful laptop series.&lt;/p&gt;

&lt;p&gt;If you’re looking for a capable and stylish laptop that balances performance, portability, and value, the MacBook Air 13.3-inch Retina (2019) remains a worthy contender.&lt;br&gt;
&lt;a href="https://www.exactsolution.com/products/macbook-air-133-inch-retina-inch-2019-core-i5-16ghz-dual-core-and-intel-uhd-graphics-617-8gb-ram-ssd-128gb-space-gray-6968dd2115a80d5aea4e6de9" rel="noopener noreferrer"&gt;https://www.exactsolution.com/products/macbook-air-133-inch-retina-inch-2019-core-i5-16ghz-dual-core-and-intel-uhd-graphics-617-8gb-ram-ssd-128gb-space-gray-6968dd2115a80d5aea4e6de9&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Redux vs Zustand: I Migrated and Here's What I Learned</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Fri, 05 Jun 2026 13:03:14 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/redux-vs-zustand-i-migrated-and-heres-what-i-learned-481</link>
      <guid>https://dev.to/exactsolutionofficial/redux-vs-zustand-i-migrated-and-heres-what-i-learned-481</guid>
      <description>&lt;p&gt;I spent about three weeks migrating a React application from Redux Toolkit to Zustand earlier this year. The app had around 40 components, 12 slices of state, and a few hundred users. It wasn't huge, but it was complex enough to feel the real differences between the two libraries.&lt;/p&gt;

&lt;p&gt;This post covers what I actually learned — what got better, what got worse, where Zustand surprised me, and where I missed Redux. Plus working code examples for the patterns that mattered most.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Quick Verdict
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use Zustand if:&lt;/strong&gt; You want minimal boilerplate, you're working on a small-to-medium app, your team is comfortable making decisions about state organization themselves, and you don't need extensive middleware ecosystems.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Use Redux Toolkit if: *&lt;/em&gt; You're working on a large app, you need predictable state management across a large team, you rely on the DevTools extensively, or you need middleware like Redux Saga / Redux Observable.&lt;/p&gt;

&lt;p&gt;The decision usually comes down to team size and predictability needs, not technical capability. Both libraries can build the same applications. They just have different opinions about how much structure you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Migrated (Honestly)
&lt;/h2&gt;

&lt;p&gt;I'll be upfront about my reasons because they affect what you should take from this comparison.&lt;/p&gt;

&lt;p&gt;The Redux Toolkit codebase wasn't broken. It worked. The migration wasn't driven by a performance problem or a bug. It was driven by team friction.&lt;/p&gt;

&lt;p&gt;Our team had grown from 2 developers to 5 in six months. The new developers found Redux Toolkit's mental model intimidating — even Redux Toolkit, which is the friendliest Redux has ever been. They struggled with concepts like slices, reducers, actions, selectors, and the dispatch pattern. Every state change required reading through 3-4 files.&lt;/p&gt;

&lt;p&gt;Three of our newer developers had used Zustand on side projects and kept asking why we couldn't use it at work.&lt;/p&gt;

&lt;p&gt;Eventually I gave in and ran a proof-of-concept migration on one feature. It went well. We migrated the rest over three sprints.&lt;/p&gt;

&lt;p&gt;This is the most important context for everything that follows: I migrated because of team friction, not because Redux was technically worse.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Differences in Code
&lt;/h2&gt;

&lt;p&gt;Before getting into lessons learned, let me show the same feature in both libraries. This is a simple shopping cart store.&lt;/p&gt;

&lt;p&gt;Redux Toolkit Version&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// store/slices/cartSlice.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PayloadAction&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CartItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CartState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartItem&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartState&lt;/span&gt; &lt;span class="o"&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;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;cartSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PayloadAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CartItem&lt;/span&gt;&lt;span class="o"&gt;&amp;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;const&lt;/span&gt; &lt;span class="nx"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&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="na"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PayloadAction&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;clearCart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clearCart&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cartSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;cartSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// store/index.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configureStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;cartReducer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./slices/cartSlice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;configureStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cartReducer&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RootState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AppDispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Component usage&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;removeItem&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./store/slices/cartSlice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RootState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AppDispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CartButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;product&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;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppDispatch&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootState&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&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="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;Add&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Cart &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;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;&lt;strong&gt;Zustand Version (Same Feature)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// stores/useCartStore.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zustand&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CartItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CartStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartItem&lt;/span&gt;&lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="nl"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;clearCart&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="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useCartStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CartStore&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;set&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="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;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="na"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&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;span class="k"&gt;return&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&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="na"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&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;clearCart&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="nf"&gt;set&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="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// Component usage&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCartStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stores/useCartStore&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CartButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;product&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;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCartStore&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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;addItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCartStore&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&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="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&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;Add&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Cart &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;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;&lt;strong&gt;What the Code Comparison Shows&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Zustand version is roughly 40% less code. No separate slice files. No store configuration. No Provider wrapper needed in your app root. No typed dispatch hook. No RootState type to maintain.&lt;/p&gt;

&lt;p&gt;For a small feature, that's significant. For a complex application, the savings compound.&lt;/p&gt;

&lt;p&gt;But code length isn't the only thing that matters. Let me get into what actually happened during the migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Got Better After Migration
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Onboarding Time Dropped Significantly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The biggest win wasn't technical — it was team-related. New developers became productive faster.&lt;/p&gt;

&lt;p&gt;With Redux Toolkit, a new developer needed to learn slices, actions, reducers, selectors, thunks, the dispatch pattern, and how all of these connected. Even with good documentation, that's a lot.&lt;/p&gt;

&lt;p&gt;With Zustand, the mental model is: "It's just a custom hook with state in it." That sentence is the entire architecture.&lt;/p&gt;

&lt;p&gt;Onboarding time for state management went from about 2 days to about 2 hours. For a growing team, this matters more than I expected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. File Count Dropped by Roughly 60%&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Redux Toolkit version of our app had a store folder with 12 slice files, 3 middleware files, a root reducer, and a store configuration file. Plus typed hooks in another file. Plus selectors organized by domain.&lt;/p&gt;

&lt;p&gt;The Zustand version has 12 store files, each fully self-contained. That's it.&lt;/p&gt;

&lt;p&gt;For a small to medium app, fewer files means less cognitive overhead. New developers can navigate the codebase faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. TypeScript Inference Got Better&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both libraries support TypeScript well, but Zustand's inference is noticeably better for selectors.&lt;/p&gt;

&lt;p&gt;In Redux Toolkit, you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootState&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You have to manually annotate RootState because the selector doesn't know what shape state has.&lt;/p&gt;

&lt;p&gt;In Zustand, you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCartStore&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TypeScript infers state from the store definition. No manual RootState maintenance. When you add a new field to a store, every selector using it gets type-checked automatically without you updating a separate types file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Testing Got Simpler&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Testing Redux requires setting up a test store, wrapping components in a Provider, and dispatching actions. Even with Redux Toolkit's setupListeners and test utilities, there's setup.&lt;/p&gt;

&lt;p&gt;Testing Zustand: just call useCartStore.getState().addItem({...}) in a test and assert against useCartStore.getState().items. No provider, no mock store, no dispatching.&lt;/p&gt;

&lt;p&gt;For unit tests, this saved time. For component integration tests, the savings were smaller because you still mount the component either way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Bundle Size Decreased&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Redux Toolkit + React Redux combination adds about 12kb gzipped to a bundle. Zustand adds about 1kb gzipped.&lt;br&gt;
For most apps, this doesn't matter. But if you're optimizing aggressively (mobile, slow networks, edge computing), it's a real difference.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Got Worse After Migration
&lt;/h2&gt;

&lt;p&gt;This is the part most migration blog posts skip. Here's what I miss about Redux.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. The Redux DevTools Are Better&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Redux DevTools is genuinely incredible. Time-travel debugging, action replay, state diffing — it's a mature, polished tool.&lt;/p&gt;

&lt;p&gt;Zustand has DevTools support via middleware (zustand/middleware/devtools), but it's noticeably less polished. Time-travel works, but the UX for action history is rougher. State diffing is less detailed.&lt;/p&gt;

&lt;p&gt;If you debug heavily through DevTools, this is a real downgrade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The "Single Source of Truth" Principle Weakened&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Redux, all state lives in one store. There's exactly one place to look for any piece of state.&lt;/p&gt;

&lt;p&gt;In Zustand, the pattern is multiple small stores. We had 12 stores after migration. The trade-off is flexibility — but the cost is that "where does X state live?" becomes a question new developers ask.&lt;/p&gt;

&lt;p&gt;This can be solved by writing internal conventions ("user state goes in useUserStore, never anywhere else"), but Redux enforces this structurally. Zustand requires discipline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Middleware Ecosystem Is Smaller&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Redux has Redux Saga, Redux Observable, Redux Logger, Redux Persist, RTK Query, and dozens of other mature middleware libraries. Zustand has middleware for persistence, DevTools, immer, and a smaller set of community options.&lt;/p&gt;

&lt;p&gt;We hit this problem when we wanted to add complex async logic that we'd previously handled with Redux Toolkit's createAsyncThunk + RTK Query. Zustand has options, but they're less mature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Predictability for Large Teams Decreased&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the same point as "single source of truth" from a different angle. Redux's verbose structure forces a specific architecture. Zustand lets you do whatever you want.&lt;/p&gt;

&lt;p&gt;In a 5-person team, this was fine. If our team grew to 15, I'd be nervous. Without enforced patterns, every developer would solve state problems slightly differently, and the codebase would diverge.&lt;/p&gt;

&lt;p&gt;For teams over 10 developers, I'd consider this a serious downside.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Async Patterns Are Less Standardized&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Redux Toolkit Query (RTK Query) is one of the best data fetching solutions in any framework. The cache invalidation, automatic refetching, optimistic updates, and TypeScript integration are all excellent.&lt;/p&gt;

&lt;p&gt;Zustand has no equivalent built-in. We ended up using TanStack Query (React Query) alongside Zustand, which works well but adds another dependency to learn.&lt;/p&gt;

&lt;p&gt;If you're already using RTK Query, replacing it with TanStack Query + Zustand is a lateral move at best.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Patterns I Wish I'd Known Earlier
&lt;/h2&gt;

&lt;p&gt;Three Zustand patterns saved significant time once I learned them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 1: Selectors with shallow for Performance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, Zustand triggers a re-render any time the selected state changes. If you select an object, every property change re-renders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This re-renders any time anything in the cart changes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;totalPrice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCartStore&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;totalPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalPrice&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;Use shallow to compare equality field-by-field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shallow&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zustand/shallow&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;totalPrice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCartStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&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="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;totalPrice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalPrice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;shallow&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without shallow, this pattern causes unnecessary re-renders. With it, performance matches what Redux's useSelector does by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 2: Persisting State to localStorage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was much easier than I expected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zustand&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createJSONStorage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zustand/middleware&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useCartStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;persist&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CartStore&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;set&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="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;addItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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="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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
      &lt;span class="na"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;id&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cart-storage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;createJSONStorage&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;localStorage&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;Three lines of middleware setup and the entire store persists automatically. No redux-persist configuration. No rehydration logic to write.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern 3: Computed/Derived State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Zustand doesn't have a useSelector equivalent for derived state, but you can compute it inside the selector:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Computed total price&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCartStore&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&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 recomputes only when items changes. For more complex derived state, you can use useMemo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCartStore&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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;totalPrice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&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;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;items&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;I missed reselect initially but ended up not needing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migration Strategy That Actually Worked
&lt;/h2&gt;

&lt;p&gt;If you're considering this migration, here's the approach that worked for us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Start With One Feature&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pick a self-contained feature (we used the shopping cart). Migrate it to Zustand while keeping Redux running for everything else. Both libraries can coexist in the same app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Run Both for a Sprint&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Live with both libraries for at least a sprint. This reveals friction points you wouldn't notice in a small POC.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Migrate Stores One at a Time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Pick stores in order of independence. Migrate the ones with fewest connections first. Don't try to migrate everything at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Migrate Selectors and Hooks Together&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you migrate a store, migrate every component using it in the same PR. Don't leave a state mid-migration. It creates confusing code where some components use useSelector and others use useCartStore.'&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5: Remove Redux Last&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only remove @reduxjs/toolkit and react-redux from package.json after every store is migrated. Run the app one more time to confirm nothing breaks.&lt;br&gt;
For us, this took three weeks of part-time work alongside normal feature development. It would have taken about two weeks if we'd dedicated full focus.&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to Migrate
&lt;/h2&gt;

&lt;p&gt;Be honest with yourself if any of these apply:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your team is happy with Redux Toolkit. "It would be nice" isn't a strong enough reason. Migrations have real costs.&lt;/li&gt;
&lt;li&gt;You have heavy RTK Query usage. TanStack Query is great but the migration cost is high.&lt;/li&gt;
&lt;li&gt;You depend on Redux DevTools for debugging. Zustand's DevTools are real but inferior.&lt;/li&gt;
&lt;li&gt;Your team is large (10+). The structural enforcement Redux provides becomes more valuable as team size grows.&lt;/li&gt;
&lt;li&gt;You have complex async middleware (Sagas, Observables). Zustand has no equivalent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all of these cases, sticking with Redux Toolkit is the smarter choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Honest Recommendation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For small-to-medium apps with small-to-medium teams:&lt;/strong&gt; Zustand wins. It's less code, faster onboarding, simpler testing, smaller bundle. The trade-offs (worse DevTools, less enforced structure) are minor at this scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For large apps with large teams:&lt;/strong&gt; Redux Toolkit is still the better choice. The enforced structure becomes a feature, not a bug. The mature ecosystem matters more. The DevTools matter more.&lt;/p&gt;

&lt;p&gt;The migration isn't a "Zustand is better" story — it's a "different tools for different stages" story. We migrated because our stage changed. If our team grew to 15 next year, we'd seriously consider migrating back.&lt;/p&gt;

&lt;p&gt;Both libraries are good. Pick the one that fits the team you have today.&lt;/p&gt;

&lt;p&gt;_If you're considering this migration or already did one, I'd love to hear what went differently for your team. The decision is much more about team dynamics than tech specs, and I think we under-discuss that.&lt;br&gt;
_&lt;/p&gt;

&lt;p&gt;About Exact Solution:&lt;br&gt;
Exact Solution is an e-commerce store specializing in refurbished electronics — laptops, smartphones, tablets, and game consoles. Our team writes about the technical decisions behind building and scaling our platform, sharing practical lessons from running a modern product business.&lt;br&gt;
Browse our &lt;a href="https://www.exactsolution.com/collections/laptops" rel="noopener noreferrer"&gt;refurbished laptops&lt;/a&gt;, refurbished smartphones, or visit &lt;a href="https://www.exactsolution.com" rel="noopener noreferrer"&gt;exactsolution.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>javascript</category>
      <category>zustand</category>
      <category>redux</category>
    </item>
    <item>
      <title>I Shipped a Bug to Production That Cost Us 3 Hours of Downtime</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Thu, 21 May 2026 12:43:22 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/i-shipped-a-bug-to-production-that-cost-us-3-hours-of-downtime-34d</link>
      <guid>https://dev.to/exactsolutionofficial/i-shipped-a-bug-to-production-that-cost-us-3-hours-of-downtime-34d</guid>
      <description>&lt;p&gt;It was a Tuesday afternoon. Nothing felt different about the deploy. Same process as always. Green tests. Approved PR. Merged to main. Deployed.&lt;br&gt;
Seventeen minutes later the alerts started.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happened
&lt;/h2&gt;

&lt;p&gt;We had a background job that processed orders and updated inventory counts across our marketplace. The job ran every few minutes and had been running reliably for months without a single issue.&lt;/p&gt;

&lt;p&gt;I had made what I genuinely believed was a minor change. A refactor. Cleaner logic, same behavior. I had tested it locally. The tests passed. A colleague reviewed it and approved it. Everything looked fine.&lt;/p&gt;

&lt;p&gt;What I had not accounted for was a race condition that only appeared under concurrent load.&lt;/p&gt;

&lt;p&gt;In local testing and in our staging environment, the job ran sequentially. In production it ran concurrently across multiple workers. The refactored logic I had written assumed sequential execution. Under concurrent load two workers would occasionally read the same inventory record at the same time, both calculate an update based on the same stale value, and both write back — the second write overwriting the first.&lt;/p&gt;

&lt;p&gt;The result was inventory counts that drifted further from reality with every job run. Orders were being accepted for products that were no longer in stock. The customer facing side kept working normally which is why the alerts took seventeen minutes to fire. By the time we caught it the damage was done.&lt;/p&gt;

&lt;p&gt;Three hours to identify, roll back, audit the affected records, correct the data, and verify the fix. Three hours of degraded service. A handful of orders that had to be manually cancelled and refunded. A postmortem that took longer than the incident itself.&lt;/p&gt;

&lt;p&gt;All of it from a change I had described in the PR as "minor refactor, no behavioral changes."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mistakes I Made
&lt;/h2&gt;

&lt;p&gt;Looking back there were several distinct failures that each independently could have prevented this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I did not read the existing tests carefully enough.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The existing tests covered the sequential case because the original code had been written with sequential execution in mind. The tests passed because I had not broken the sequential behavior — I had broken the concurrent behavior that the tests never covered. I looked at green tests and assumed coverage. Green tests mean the tested cases pass. They say nothing about the cases that were not tested.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I assumed staging was representative of production.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Our staging environment runs a single worker. Production runs multiple. This difference had never mattered before because nothing we had deployed before this change was sensitive to concurrency. I knew staging was a single worker. I did not think about whether that mattered for this specific change. It did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I described the change inaccurately in the PR.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Minor refactor, no behavioral changes" is the kind of description that makes reviewers less careful not more. My colleague approved it quickly because the description signaled low risk. If I had described it accurately — "refactoring the inventory update logic, please check whether the new approach handles concurrent writes correctly" — the review would have been different. The description I wrote was not intentionally misleading. It reflected my own incorrect assessment of the risk. That is worse in some ways. I had convinced myself it was minor before I convinced anyone else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I had no concurrency testing in my local verification process.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I ran the job locally once and watched it complete successfully. I did not run it concurrently. I did not run it under load. I did not simulate multiple workers. Running a background job once in a local environment and calling it tested is not testing the job — it is testing one execution path under ideal conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Changed After
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We added a staging environment worker count that matches production.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This sounds obvious in retrospect. It was not obvious until it cost us three hours. Staging now runs the same number of workers as production for any service where concurrency matters. The environment is still not a perfect replica but the concurrency profile now matches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We added explicit concurrency tests for any job that touches shared state.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not just unit tests for the logic. Integration tests that spin up multiple workers, run them simultaneously against the same test data, and verify the outcome is consistent. These tests are slower. They are also the only tests that would have caught this specific failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We changed how we describe PR risk in reviews.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We added a required field to our PR template: "Concurrency considerations." It can be filled with "N/A — this change does not touch shared state" which takes ten seconds. For changes that do touch shared state it forces the author to think about and articulate the concurrency implications before a reviewer sees the code. The reviewer then knows to focus there specifically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We introduced a pre-deploy checklist for background jobs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a long checklist. Four questions. Does this job touch shared state? Does the logic assume sequential execution? Has it been tested under concurrent load? Does the rollback procedure work without manual data correction? The checklist takes two minutes. The incident it is designed to prevent took three hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Thing I Keep Coming Back To
&lt;/h2&gt;

&lt;p&gt;The bug itself was not the most interesting part of this story.&lt;br&gt;
The most interesting part is how many independent checkpoints it sailed through without being caught.&lt;/p&gt;

&lt;p&gt;I did not catch it in my own review of the change. My colleague did not catch it in the PR review. The automated tests did not catch it. The staging deployment did not catch it.&lt;/p&gt;

&lt;p&gt;Each of those checkpoints failed for a different reason. My self review failed because I had already decided the change was minor and was not looking carefully. The PR review failed because my description set a low risk frame that the reviewer accepted. The automated tests failed because they covered the wrong execution model. Staging failed because the environment did not match production on the one dimension that mattered.&lt;br&gt;
A failure that gets through four independent checkpoints is not bad luck. It is a systems problem. The checkpoints were not actually independent — they were all downstream of my initial incorrect assessment that the change was low risk. Once I had decided it was minor, every subsequent checkpoint was biased toward confirming that assessment.&lt;/p&gt;

&lt;p&gt;This is the uncomfortable version of the lesson. It is not just "write better tests" or "stage more carefully." It is that your own confidence in a change actively degrades the quality of the checks that follow it. The times you are most certain a change is safe are the times you most need to deliberately stress test that certainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Do Differently Now
&lt;/h2&gt;

&lt;p&gt;Before merging anything that touches background jobs, queues, or shared state I now ask one question regardless of how minor the change feels:&lt;br&gt;
What would have to be true about the execution environment for this to fail silently?&lt;/p&gt;

&lt;p&gt;Not loudly. Not with an immediate error that gets caught in tests. Silently — in a way that produces correct looking output under normal conditions and wrong output under specific conditions that do not exist in development or staging.&lt;/p&gt;

&lt;p&gt;That question changes how I look at a change. It forces me to think about the gap between the environment where I tested and the environment where it will run.&lt;/p&gt;

&lt;p&gt;It would have caught this one. It has caught two potential issues since. Neither of them made it to production.&lt;br&gt;
The best lesson from a production incident is not a new process. It is a new question you ask yourself before you need the process.&lt;/p&gt;

&lt;p&gt;Exact Solution is a certified refurbished electronics marketplace shipping across Europe. We stock the &lt;a href="https://www.exactsolution.com/collections/laptops" rel="noopener noreferrer"&gt;best refurbished laptops&lt;/a&gt; from Apple, Dell, HP, and Lenovo — all fully tested and ready to ship.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>discuss</category>
      <category>ai</category>
    </item>
    <item>
      <title>https://dev.to/exactsolutionofficial/the-cursor-3-features-nobody-is-talking-about-yet-55e</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Wed, 20 May 2026 12:08:06 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/httpsdevtoexactsolutionofficialthe-cursor-3-features-nobody-is-talking-about-yet-55e-27g4</link>
      <guid>https://dev.to/exactsolutionofficial/httpsdevtoexactsolutionofficialthe-cursor-3-features-nobody-is-talking-about-yet-55e-27g4</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/exactsolutionofficial/the-cursor-3-features-nobody-is-talking-about-yet-55e" class="crayons-story__hidden-navigation-link"&gt;The Cursor 3 Features Nobody Is Talking About Yet&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/exactsolutionofficial" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3763937%2Ff9a19ec9-ac30-460d-80cb-ee90fbddabd4.png" alt="exactsolutionofficial profile" class="crayons-avatar__image" width="300" height="300"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/exactsolutionofficial" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Exact Solution
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Exact Solution
                
              
              &lt;div id="story-author-preview-content-3709031" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/exactsolutionofficial" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3763937%2Ff9a19ec9-ac30-460d-80cb-ee90fbddabd4.png" class="crayons-avatar__image" alt="" width="300" height="300"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Exact Solution&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/exactsolutionofficial/the-cursor-3-features-nobody-is-talking-about-yet-55e" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 20&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/exactsolutionofficial/the-cursor-3-features-nobody-is-talking-about-yet-55e" id="article-link-3709031"&gt;
          The Cursor 3 Features Nobody Is Talking About Yet
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/cursor"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;cursor&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/exactsolutionofficial/the-cursor-3-features-nobody-is-talking-about-yet-55e#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>The Cursor 3 Features Nobody Is Talking About Yet</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Wed, 20 May 2026 11:41:54 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/the-cursor-3-features-nobody-is-talking-about-yet-55e</link>
      <guid>https://dev.to/exactsolutionofficial/the-cursor-3-features-nobody-is-talking-about-yet-55e</guid>
      <description>&lt;p&gt;Everyone is writing about the Agents Window. The parallel agents. The cloud handoff. The rebuilt interface from scratch under the codename Glass.&lt;/p&gt;

&lt;p&gt;Those are the headline features and they deserve the coverage. But after spending serious time in Cursor 3 since it shipped on April 2, 2026, the features that have actually changed how I work are not the ones in the announcement post.&lt;/p&gt;

&lt;p&gt;Here are the Cursor 3 features that are quietly doing the most useful work — and that most developers have either not found or not fully understood yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Design Mode — Point at UI Elements Instead of Describing Them
&lt;/h2&gt;

&lt;p&gt;This one is buried in the changelog and barely mentioned in the main announcement.&lt;/p&gt;

&lt;p&gt;In the Agents Window, you can activate Design Mode to annotate and target UI elements directly in the browser. Instead of writing a prompt like "the button in the top right corner of the navigation bar that appears on hover" — you point at it. The agent knows exactly what you are referring to.&lt;/p&gt;

&lt;p&gt;This sounds small. It is not small.&lt;/p&gt;

&lt;p&gt;Anyone who has spent time trying to describe UI context to an AI model knows the problem. You spend more words explaining what you are looking at than it would take to just fix it yourself. Design Mode collapses that entirely. You point. The agent acts.&lt;/p&gt;

&lt;p&gt;To access it: open the Agents Window with Cmd+Shift+P -&amp;gt; Agents Window, then activate Design Mode from the agent toolbar. It only works in the Agents Window, not the traditional IDE view — which is probably why most developers using the old IDE interface have not found it yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The PR Review Experience That Replaces Your Whole Review Workflow
&lt;/h2&gt;

&lt;p&gt;Cursor 3 shipped a PR review experience that most developers are treating as a minor feature. It is not minor.&lt;/p&gt;

&lt;p&gt;You can now take a PR from creation to merge entirely within Cursor. The agent reviews the changes, flags issues, suggests improvements, and tracks the full lifecycle of the PR without you switching to GitHub, back to the terminal, back to GitHub, back to the editor.&lt;/p&gt;

&lt;p&gt;The context switching tax in code review is enormous and almost completely invisible because it has always been there. Every time you leave the editor to check a PR comment and come back, you pay a cost. Cursor 3 eliminates that context switch.&lt;/p&gt;

&lt;p&gt;The Bugbot integration makes this sharper. With the high effort setting — which you can now customize per team or per individual — Bugbot finds significantly more bugs per run than default. The tradeoff is cost and time. But for critical PRs where a missed bug is expensive, the option to dial up the effort is genuinely useful and not something most PR review tools give you control over.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Parallel Agents on Plans — The Feature That Changes How You Scope Work
&lt;/h2&gt;

&lt;p&gt;When you create a plan in Cursor 3, the agents execute steps in parallel where dependencies allow. This sounds like a performance improvement. It is actually a workflow change.&lt;/p&gt;

&lt;p&gt;Because steps run in parallel, you start thinking about work differently. Instead of a linear sequence of tasks, you think about the dependency graph. What needs to be done before what? What can happen simultaneously?&lt;br&gt;
This is how senior engineers think about complex work. Cursor 3 essentially externalizes that mental model into the tool. You write the plan. The tool figures out what can run in parallel. You watch multiple things get built at once.&lt;/p&gt;

&lt;p&gt;The quick action pills that shipped alongside this — common workflow actions surfaced as one click options rather than typed commands — reduce the friction of directing parallel work significantly. Less typing, faster iteration, more time watching things actually happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Composer 2 Is the Real Engine — And Most Developers Do Not Understand What It Is
&lt;/h2&gt;

&lt;p&gt;Cursor 3 gets the attention. Composer 2, which shipped on March 19 and powers Cursor 3's agents, is the part that actually explains why the agents feel different.&lt;/p&gt;

&lt;p&gt;Composer 2 is not a wrapper around a frontier model. It is Cursor's own model — built on Kimi K2.5 from Moonshot AI as a base, then continued pre-training on a code heavy data mix, followed by large scale reinforcement learning in realistic Cursor sessions. Around 75 percent of the total compute came from Cursor's own training, not the base model.&lt;/p&gt;

&lt;p&gt;What this means in practice is that Composer 2 has been trained on how developers actually use Cursor — not just on code in the abstract. It knows the tool it is running in. It understands the workflows. It has been reinforced on real Cursor sessions.&lt;/p&gt;

&lt;p&gt;This is a fundamentally different approach from routing your requests to Claude or GPT and hoping the model understands your IDE context. Composer 2 was built for this environment specifically. The capability jump on complex multi-file tasks is noticeable — and it is not placebo.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Environment Version History — The Underrated Safety Net
&lt;/h2&gt;

&lt;p&gt;This one shipped quietly in the most recent update and I have not seen anyone write about it yet.&lt;/p&gt;

&lt;p&gt;Every development environment in Cursor now has its own version history that you can review and roll back. If an agent configures your environment incorrectly — installs the wrong dependency version, misconfigures a tool, breaks something in the setup — you can roll back to a previous environment state rather than debugging what changed.&lt;/p&gt;

&lt;p&gt;For solo developers and small teams this is genuinely useful. Environment configuration bugs are some of the most time consuming to diagnose because the symptoms appear in your code but the cause is in your setup. Version history on the environment itself gives you a direct path back to a known good state.&lt;/p&gt;

&lt;p&gt;Admins can also restrict rollback permissions to admins only — which matters for teams where environment consistency is critical and you do not want individual developers rolling back shared configurations without oversight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Feature I Am Still Watching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cursor is rolling out automated environment configuration in private beta for Enterprise teams — where it inspects your repos, figures out the tools and dependencies required, and produces a configuration you can edit and version. As it configures your environment, it asks questions, flags missing credentials, and validates that your environment is set up properly.&lt;/p&gt;

&lt;p&gt;This is not widely available yet. But if it works as described it solves one of the most painful parts of onboarding new developers or spinning up new projects — the environment setup that always takes longer than anyone planned and breaks in ways that are specific to the machine, not the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Cursor 3 Actually Is&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The announcement framed Cursor 3 as a unified workspace for building software with agents. That is accurate but understates the shift.&lt;br&gt;
The traditional IDE view still exists and still works. You can switch back at any time. But the Agents Window represents a genuinely different model of how software gets built — you as the architect directing agents, not you as the typist writing every line.&lt;/p&gt;

&lt;p&gt;The Agents Window, parallel execution, cloud agent handoff, Design Mode, and built-in Git are all meaningful additions that move Cursor from a smart IDE with AI toward something closer to an agent orchestration platform that happens to have an excellent IDE inside it. exactsolution&lt;br&gt;
The features nobody is talking about are the ones that make that shift feel real in daily use rather than just impressive in a demo. Design Mode, PR review lifecycle, environment version history, parallel plan execution — these are the features that change Tuesday afternoon, not just the ones that change the keynote slide.&lt;/p&gt;

&lt;p&gt;Exact Solution is a certified refurbished electronics marketplace shipping across Europe. We stock the &lt;a href="https://www.exactsolution.com/collections/laptops" rel="noopener noreferrer"&gt;best refurbished laptops&lt;/a&gt; from Apple, Dell, HP, and Lenovo — all fully tested and ready to ship.&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>5 JavaScript habits that look clean but quietly wreck your code. Sequential awaits, unnecessary state, missing cleanup — real examples and fixes.
https://dev.to/exactsolutionofficial/5-javascript-habits-that-look-clean-but-quietly-wreck-your-code-57i0</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Tue, 19 May 2026 12:32:28 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/5-javascript-habits-that-look-clean-but-quietly-wreck-your-code-sequential-awaits-unnecessary-fbn</link>
      <guid>https://dev.to/exactsolutionofficial/5-javascript-habits-that-look-clean-but-quietly-wreck-your-code-sequential-awaits-unnecessary-fbn</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/exactsolutionofficial/5-javascript-habits-that-look-clean-but-quietly-wreck-your-code-57i0" class="crayons-story__hidden-navigation-link"&gt;5 JavaScript Habits That Look Clean But Quietly Wreck Your Code&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/exactsolutionofficial" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3763937%2Ff9a19ec9-ac30-460d-80cb-ee90fbddabd4.png" alt="exactsolutionofficial profile" class="crayons-avatar__image" width="300" height="300"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/exactsolutionofficial" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Exact Solution
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Exact Solution
                
              
              &lt;div id="story-author-preview-content-3693879" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/exactsolutionofficial" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3763937%2Ff9a19ec9-ac30-460d-80cb-ee90fbddabd4.png" class="crayons-avatar__image" alt="" width="300" height="300"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Exact Solution&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/exactsolutionofficial/5-javascript-habits-that-look-clean-but-quietly-wreck-your-code-57i0" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/exactsolutionofficial/5-javascript-habits-that-look-clean-but-quietly-wreck-your-code-57i0" id="article-link-3693879"&gt;
          5 JavaScript Habits That Look Clean But Quietly Wreck Your Code
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/react"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;react&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/exactsolutionofficial/5-javascript-habits-that-look-clean-but-quietly-wreck-your-code-57i0#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>5 JavaScript Habits That Look Clean But Quietly Wreck Your Code</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Mon, 18 May 2026 13:30:06 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/5-javascript-habits-that-look-clean-but-quietly-wreck-your-code-57i0</link>
      <guid>https://dev.to/exactsolutionofficial/5-javascript-habits-that-look-clean-but-quietly-wreck-your-code-57i0</guid>
      <description>&lt;p&gt;You have been writing JavaScript for a while now. Your code is readable. Your colleagues do not complain. Your pull requests get approved without much debate.&lt;/p&gt;

&lt;p&gt;And yet something keeps going wrong in production that you cannot quite explain.&lt;/p&gt;

&lt;p&gt;The problem might not be what you are doing wrong. It might be what you are doing right — in the wrong context.&lt;/p&gt;

&lt;p&gt;Here are five habits that look perfectly reasonable and are actively making your codebase worse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using async/await everywhere without thinking about parallelism&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This one is everywhere. And honestly it looks so clean that most people never question it.&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%2F5gqvz43zu9jz0nrvu0kb.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%2F5gqvz43zu9jz0nrvu0kb.png" alt=" " width="752" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looks fine right? Three clean awaits. Very readable. Very modern.&lt;br&gt;
Except you just made three sequential network calls that have absolutely no dependency on each other. You are waiting for the user to load before even starting to fetch posts. Then waiting for posts before even thinking about comments.&lt;/p&gt;

&lt;p&gt;On a fast connection with a healthy server this adds maybe 100ms. On a slow mobile connection it adds 600ms. And users on slow connections are already struggling.&lt;/p&gt;

&lt;p&gt;The fix is obvious once you see it:&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%2F0x1ur8k0sjom55bp7hy7.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%2F0x1ur8k0sjom55bp7hy7.png" alt=" " width="791" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Same result. All three requests fire simultaneously. I have seen this single change cut perceived load time in half on real production apps.&lt;br&gt;
The irony is that sequential awaits feel more controlled. More readable. More intentional. Which is exactly why people keep writing them without thinking about what is actually happening under the hood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Storing everything in useState when it is not actually state&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;React developers do this constantly. I did it constantly for longer than I would like to admit.&lt;/p&gt;

&lt;p&gt;Something needs to persist across renders so you reach for useState. Makes sense. Except not everything that persists is actually state.&lt;/p&gt;

&lt;p&gt;Derived values, computed results, values that come directly from props — none of these need to be in state. But they end up there anyway because useState is the first tool we reach for.&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%2Fcqb5vo90kop3b6jdr4nc.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%2Fcqb5vo90kop3b6jdr4nc.png" alt=" " width="770" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you need to keep this in sync with firstName and lastName manually. And you will forget. And then you will have a bug that only appears when the user updates their first name without their last name changing first. And you will spend 45 minutes debugging what should not exist at all.&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%2Few7qov0jom4ljj9hglxb.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%2Few7qov0jom4ljj9hglxb.png" alt=" " width="792" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Done. No state. No sync. No bug.&lt;br&gt;
The rule I try to follow now — if a value can be computed from existing state or props, it should not be state. Every piece of unnecessary state is a future synchronization bug waiting to happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Writing utility functions inside components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This happens when you are moving fast and it feels fine in the moment.&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%2F92z2mub4px2xc6028p20.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%2F92z2mub4px2xc6028p20.png" alt=" " width="747" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clean looking component. Self contained. Everything in one place.&lt;br&gt;
Except formatDate gets recreated on every single render. Every time the parent re-renders, every time any state changes, every time anything touches this component — a brand new function is created and immediately thrown away.&lt;/p&gt;

&lt;p&gt;On one component this is basically nothing. On a list rendering 200 UserCards with multiple utility functions each, you are creating and garbage collecting hundreds of functions per render cycle for no reason.&lt;br&gt;
Move utility functions outside the component. If they need to be reused elsewhere, they were always going to end up there anyway. If they do not, at least they are created once.&lt;/p&gt;

&lt;p&gt;The muscle memory of writing everything inside the component is hard to break. But once you start noticing the pattern it is impossible to stop seeing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Not cleaning up event listeners and subscriptions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the one that creates ghost bugs. The kind that appears three months after you shipped a feature and only happens on specific navigation patterns that your tests never covered.&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%2Fzs2zvg9pblmun6rcurxh.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%2Fzs2zvg9pblmun6rcurxh.png" alt=" " width="727" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every time this component mounts it adds a resize listener. Every time it unmounts — which in a typical SPA happens constantly — the listener stays. Navigate to this page ten times and you have ten resize listeners all firing simultaneously calling handleResize on a component that no longer exists.&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%2Fcv27rkucujhew77jw64s.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%2Fcv27rkucujhew77jw64s.png" alt=" " width="750" height="150"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cleanup function is not optional. It is the entire point.&lt;br&gt;
I once inherited a codebase where certain pages became noticeably slower the longer you used the app. The cause was an analytics tracking listener being added on every route change and never removed. After about 20 minutes of normal app usage you had 80 to 100 duplicate listeners all firing on every user interaction.&lt;/p&gt;

&lt;p&gt;Nobody had noticed because nobody tested long sessions. They opened the app, clicked around for 2 minutes, and called it done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Using console.log for debugging and shipping it to production&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This sounds too obvious to include. It is not.&lt;/p&gt;

&lt;p&gt;console.log in production is not just a minor embarrassment. In tight loops, frequent event handlers, or anywhere with high call frequency it is a real performance drain. The browser has to format the output, write to the console buffer, and do this every single time even when the devtools are closed.&lt;/p&gt;

&lt;p&gt;But the more common version of this problem is not the obvious console.log("user data:", user) sitting in your render function. It is the logging that was added during a debugging session six months ago, wrapped in some condition that seemed safe, and has been silently firing in production ever since.&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%2Fayr09fem6mwvjxodst4d.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%2Fayr09fem6mwvjxodst4d.png" alt=" " width="785" height="127"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are going to log, at least make it conditional. Better yet, use a proper logging library that handles environment awareness and gives you log levels you can actually control.&lt;br&gt;
The real fix is a pre-commit hook or ESLint rule that flags console.log statements before they ever reach main. Set it up once and never think about it again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final thought&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;None of these patterns are obviously wrong when you first write them. That is the whole point.&lt;/p&gt;

&lt;p&gt;Sequential awaits feel controlled. State feels like the right place to store things. Utility functions inside components feel self contained.&lt;/p&gt;

&lt;p&gt;Missing cleanup feels like an edge case. Console logs feel temporary.&lt;/p&gt;

&lt;p&gt;They all made sense at the time. They all compound over time.&lt;/p&gt;

&lt;p&gt;The best code reviews are the ones where someone asks "why is this here" about something that felt completely natural when it was written. Build that habit yourself and you will catch most of these before they become production problems.&lt;/p&gt;

&lt;p&gt;Exact Solution is a certified refurbished electronics marketplace shipping across Europe. We stock the &lt;a href="https://www.exactsolution.com/collections/laptops" rel="noopener noreferrer"&gt;best refurbished laptops&lt;/a&gt; from Apple, Dell, HP, and Lenovo — all fully tested and ready to ship. We keep our code as clean as our laptops — mostly&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>MacBook Pro 16-inch 2019 for Students: Good Choice or Too Much?</title>
      <dc:creator>Exact Solution</dc:creator>
      <pubDate>Thu, 14 May 2026 11:39:01 +0000</pubDate>
      <link>https://dev.to/exactsolutionofficial/macbook-pro-16-inch-2019-for-students-good-choice-or-too-much-4kcc</link>
      <guid>https://dev.to/exactsolutionofficial/macbook-pro-16-inch-2019-for-students-good-choice-or-too-much-4kcc</guid>
      <description>&lt;p&gt;Choosing the right laptop for student life depends on your study needs. Some students only need a laptop for assignments, online classes, browsing, and presentations. Others need stronger performance for design, programming, video editing, engineering, or creative work.&lt;/p&gt;

&lt;p&gt;The refurbished MacBook Pro 16-inch 2019 can be a great option for students who need power, a large display, and long-term usability. But for basic users, it may be more than necessary.&lt;/p&gt;

&lt;p&gt;Why Students May Like the MacBook Pro 16-inch 2019&lt;/p&gt;

&lt;p&gt;The MacBook Pro 16-inch 2019 is a premium laptop with strong performance, a large Retina display, 16GB RAM, and 1TB SSD storage. This makes it useful for students who want one device for study, projects, entertainment, and even freelance work.&lt;/p&gt;

&lt;p&gt;For normal student tasks like writing documents, using Google Workspace, attending online classes, researching, and preparing presentations, this laptop is easily powerful enough. It also performs well for heavier tasks such as coding, video editing, photo editing, music production, and design software.&lt;/p&gt;

&lt;p&gt;Performance: Strong for Demanding Students&lt;/p&gt;

&lt;p&gt;The MacBook Pro 16-inch 2019 Core i9 is best suited for students who need more than a basic laptop. If you are studying computer science, architecture, engineering, graphic design, photography, film, or media production, the extra performance can be useful.&lt;/p&gt;

&lt;p&gt;The 16GB RAM helps with multitasking, while the 1TB SSD gives enough storage for apps, documents, university files, media, and large project folders. For students who work on creative or technical projects, this is a clear advantage.&lt;/p&gt;

&lt;p&gt;Display: Great for Study and Creative Work&lt;/p&gt;

&lt;p&gt;The 16-inch Retina display is one of the biggest benefits of this MacBook. A larger screen makes it easier to work with multiple windows, write assignments, edit videos, code, or attend online classes while taking notes.&lt;/p&gt;

&lt;p&gt;For design and media students, the display quality is especially useful because it offers sharp visuals and good color accuracy.&lt;/p&gt;

&lt;p&gt;Portability: Powerful, But Not Lightweight&lt;/p&gt;

&lt;p&gt;The MacBook Pro 16-inch 2019 is portable, but it is not the lightest student laptop. It is bigger and heavier than a MacBook Air. If you carry your laptop all day between classes, this may matter.&lt;/p&gt;

&lt;p&gt;However, if you mostly study at home, in the library, or at a desk, the larger screen and better performance can be worth the extra size.&lt;/p&gt;

&lt;p&gt;Is It Too Much for Students?&lt;/p&gt;

&lt;p&gt;For some students, yes. If you only need a laptop for browsing, notes, online classes, and basic assignments, a MacBook Air may be enough.&lt;/p&gt;

&lt;p&gt;But if you want a laptop that can handle university work, creative projects, multitasking, and future professional use, the MacBook Pro 16-inch 2019 is still a strong choice.&lt;/p&gt;

&lt;p&gt;Refurbished MacBook Pro: Better Value for Students&lt;/p&gt;

&lt;p&gt;Buying a new high-performance MacBook can be expensive. That is why a refurbished MacBook Pro can be a smart choice for students who want premium performance at a better price.&lt;/p&gt;

&lt;p&gt;You can explore refurbished electronics online or choose this MacBook Pro 16-inch 2019 Core i9 if you need strong performance and large storage.&lt;/p&gt;

&lt;p&gt;Final Verdict&lt;/p&gt;

&lt;p&gt;The MacBook Pro 16-inch 2019 for students is a good choice for those who need power, a large screen, and reliable performance. It is ideal for creative, technical, and professional study fields.&lt;/p&gt;

&lt;p&gt;However, it may be too much for students with only basic needs. If you want a lightweight laptop for simple tasks, choose a MacBook Air. If you want performance, storage, and a premium display, the MacBook Pro 16-inch 2019 is still worth considering.&lt;br&gt;
&lt;a href="https://www.exactsolution.com/products/macbook-pro-16-inch-inch-2019-intel-core-i9-23ghz-and-amd-radeon-pro-5300m-16gb-ram-ssd-1tb-space-grey-69afcd2477c606cf7dcdbb64" rel="noopener noreferrer"&gt;https://www.exactsolution.com/products/macbook-pro-16-inch-inch-2019-intel-core-i9-23ghz-and-amd-radeon-pro-5300m-16gb-ram-ssd-1tb-space-grey-69afcd2477c606cf7dcdbb64&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
