<?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: Mittal Technologies</title>
    <description>The latest articles on DEV Community by Mittal Technologies (@mittal_technologies).</description>
    <link>https://dev.to/mittal_technologies</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%2F3888395%2F6dcf366c-b332-40ff-9b07-dddcf1445cdf.png</url>
      <title>DEV Community: Mittal Technologies</title>
      <link>https://dev.to/mittal_technologies</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mittal_technologies"/>
    <language>en</language>
    <item>
      <title>Next.js + Contentful vs Next.js + Sanity: A Real Performance Comparison</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Mon, 20 Jul 2026 09:29:01 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/nextjs-contentful-vs-nextjs-sanity-a-real-performance-comparison-2ebc</link>
      <guid>https://dev.to/mittal_technologies/nextjs-contentful-vs-nextjs-sanity-a-real-performance-comparison-2ebc</guid>
      <description>&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%2Fiqgf3mcvd9snwg48949a.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%2Fiqgf3mcvd9snwg48949a.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Everyone recommends "just pick a headless CMS and pair it with Next.js" like it's a five-minute decision. It isn't. When our team was scoping a rebuild for a client who wanted faster page loads without losing editorial flexibility, we actually benchmarked Contentful and Sanity side by side instead of going with whatever had the shinier marketing site. As a &lt;a href="https://mittaltechnologies.com/service/development" rel="noopener noreferrer"&gt;website development company in Ludhiana&lt;/a&gt; that ships client projects on both stacks, here's what the numbers and the day-to-day developer experience actually looked like.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We built the same landing page and blog template twice — once pulling content from Contentful's REST/GraphQL API, once from Sanity's GROQ-powered API — both deployed on Next.js 14 with ISR enabled. Same hosting, same image optimization pipeline, same Lighthouse testing conditions. We wanted a genuinely fair comparison, which meant resisting the urge to over-optimize one stack more than the other just because it happened to be more familiar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Contentful fetch example
const entries = await client.getEntries({
  content_type: 'blogPost',
  order: '-fields.publishDate',
  limit: 10
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Sanity fetch example using GROQ
const posts = await sanityClient.fetch(
  `*[_type == "post"] | order(publishDate desc)[0...10]`
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;What We Found on Speed&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Sanity's GROQ queries came back noticeably faster for nested, relational content — think blog posts with linked authors, categories, and related posts. Contentful handled flat content types just as well, but once we needed two or three levels of reference resolution, response times crept up unless we restructured the content model. Neither was "slow," to be clear — we're talking differences in the 100-300ms range, which matters for Core Web Vitals but won't make or break a small brochure site.&lt;/p&gt;

&lt;p&gt;Cache behavior differed too. Contentful's CDN-backed delivery API is reliably fast for read-heavy traffic once cached, but the first uncached request after a content update showed a small but measurable lag. Sanity's CDN caching behaved similarly, though its real-time preview mode occasionally introduced its own overhead during active editing sessions, which is worth knowing if your editorial team previews frequently before publishing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Developer Experience Differences&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Sanity's Studio is code-based and highly customizable, which developers on our team preferred&lt;/li&gt;
&lt;li&gt;Contentful's editor UI is more polished out of the box for non-technical content teams&lt;/li&gt;
&lt;li&gt;GROQ has a steeper learning curve than Contentful's query language&lt;/li&gt;
&lt;li&gt;Contentful's free tier is more generous for small projects; Sanity's pricing scales differently at volume&lt;/li&gt;
&lt;li&gt;Webhooks and real-time preview worked more smoothly with Sanity in our testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing that surprised us: image handling. Sanity's built-in image pipeline with hotspot cropping saved real development time compared to manually configuring Contentful's image API parameters. Small thing, but it adds up across a project with hundreds of images, especially when a client's content team is uploading new photography every week without a developer double-checking crop point.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Content Modeling Differences That Actually Matter&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Beyond raw speed, the two platforms encourage slightly different habits. Contentful's content type builder nudges you toward more rigid, form-like structures, which content editors generally find intuitive. Sanity's schema-as-code approach gives developers more control over validation and conditional fields, but it does mean content modeling becomes a developer task rather than something a content strategist can adjust independently. If your team wants editors to be able to add new fields without filing a dev ticket, that's a real consideration, not a minor one.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Cost at Scale&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Pricing structures diverge more than people expect once a project grows past a handful of content types. Contentful bills primarily around API calls and environments, which can get expensive quickly for high-traffic sites unless caching is well configured. Sanity's pricing leans more on dataset size and bandwidth, which tends to favor content-heavy sites with moderate traffic over traffic-heavy sites with lean content. Neither pricing model is a trap exactly, but both can surprise a client who scaled past their original estimate without revisiting the numbers, so we now build a rough cost projection into every proposal rather than leaving it as a line item nobody checks until the first real invoice arrives.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;When to Pick Which&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Contentful tends to make sense for larger marketing teams who want a familiar, low-friction editor and don't mind paying for that convenience. Sanity fits teams with developers comfortable customizing the editing environment and who need flexible, relational content modeling. We've built client projects on both, and honestly, neither is objectively "better" — it depends on team structure more than raw performance. If your content team is non-technical and change-averse, Contentful reduces friction. If your developers want control, Sanity gives them more of it.&lt;/p&gt;

&lt;p&gt;We put a version of this comparison into practice while rebuilding infrastructure for a client project, where content flexibility mattered more than editorial simplicity, so Sanity ended up being the right call for that particular build — the kind of judgment call a &lt;a href="https://mittaltechnologies.com/service/development" rel="noopener noreferrer"&gt;software company in Ludhiana&lt;/a&gt; makes on a project-by-project basis rather than defaulting to one platform every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What We'd Tell a Team Starting From Scratch&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Don't pick a CMS based on a blog post, including this one. Build a small prototype of your actual content model — not a generic blog template — in both platforms before committing budget to either. The differences that matter most tend to show up in your specific content relationships, not in generic benchmarks. If your team doesn't have the bandwidth to run that kind of trial, our &lt;a href="https://mittaltechnologies.com/service/development" rel="noopener noreferrer"&gt;website designing company in Ludhiana&lt;/a&gt; has done this exact exercise enough times to shortcut the process for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Wrapping Up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Benchmark your own content structure before committing to either platform — generic comparisons like this one are a starting point, not a final answer. If you want a second opinion on your specific setup, our &lt;a href="https://mittaltechnologies.com/service/development" rel="noopener noreferrer"&gt;website developer in Ludhiana&lt;/a&gt; team has run this exact comparison for multiple client stacks and can walk through what fits yours. Reaching out to us directly is a faster route than another week of research.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Quick reference: fetching with revalidation in Next.js
export async function getStaticProps() {
  const data = await client.getEntries({ content_type: 'post' });
  return { props: { data }, revalidate: 60 };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>nextjs</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>storyblokchallenge</category>
    </item>
    <item>
      <title>MCP Explained for Web Developers</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Thu, 16 Jul 2026 08:24:52 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/mcp-explained-for-web-developers-47fh</link>
      <guid>https://dev.to/mittal_technologies/mcp-explained-for-web-developers-47fh</guid>
      <description>&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%2Fbh08f86dw0kfkmza6bgp.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%2Fbh08f86dw0kfkmza6bgp.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
I ignored MCP for longer than I should have because the acronym soup around AI tooling right now is genuinely exhausting, and I assumed it was another framework-shaped thing I'd need to relearn in six months. Then I actually built something with it over a weekend, a small internal tool connecting Claude to our project's data, and it clicked faster than I expected. If you're a web developer who's been putting this off the same way I did, here's the version of the explanation I wish someone had given me first.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;What MCP Actually Is&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Model Context Protocol is an open standard for connecting AI models to external tools and data sources in a consistent, predictable way, the kind of infrastructure a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;best digital marketing company in Ludhiana&lt;/a&gt; building AI-powered client tooling now has to think about too. Before MCP, every AI integration you built was custom, your own function-calling setup, your own auth handling, your own way of describing what the model could do. MCP standardizes that.&lt;/p&gt;

&lt;p&gt;Think of it roughly like this: if REST gave web developers a consistent way to expose data over HTTP, MCP gives AI applications a consistent way to expose tools and data to a model, regardless of which AI provider is calling it.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;The Core Pieces&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;An MCP setup has a few consistent parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MCP Server —&lt;/strong&gt; exposes tools, resources, and prompts that a model can use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Client —&lt;/strong&gt; the AI application (like Claude) that connects to one or more servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tools —&lt;/strong&gt; functions the model can call, similar conceptually to API endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resources —&lt;/strong&gt; data the model can read, like files or database records&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transport —&lt;/strong&gt; how the client and server actually communicate, commonly over stdio locally or HTTP/SSE for remote servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've built a REST API before, tools will feel familiar. If you've worked with GraphQL resolvers, resources will feel familiar too.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;A Minimal Example&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here's roughly what a basic MCP server tool definition looks like in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new McpServer({
  name: "project-tools",
  version: "1.0.0"
});

server.tool(
  "get_task_status",
  "Retrieve the current status of a project task by ID",
  { taskId: z.string() },
  async ({ taskId }) =&amp;gt; {
    const task = await db.tasks.findById(taskId);
    return {
      content: [{ type: "text", text: JSON.stringify(task) }]
    };
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole shape of it. You define a tool, describe what it does in plain language (the model reads that description to decide when to use it), specify the expected input, and return a result. No custom prompt engineering to teach the model your API shape, no brittle regex parsing of model output to extract function calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why This Matters If You're Building AI Features&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before MCP, adding a new data source to an AI feature usually meant a lot of repeated, provider-specific work, exactly the kind of overhead a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;digital marketing agency Ludhiana&lt;/a&gt; building custom automation tooling for clients wants to avoid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing custom function definitions for whichever provider's function-calling format you were using&lt;/li&gt;
&lt;li&gt;Rebuilding that integration if you switched providers or supported multiple ones&lt;/li&gt;
&lt;li&gt;Handling auth and connection logic separately for every single integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With MCP, you write the server once, and any MCP-compatible client can use it, without you rebuilding provider-specific glue code every time the AI landscape shifts, which right now is often.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A Realistic Use Case&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Say you're building an internal tool where a team wants to ask Claude questions about your company's live inventory data instead of digging through a dashboard. Without MCP, you'd be writing custom prompt templates, stuffing inventory data into context, or a bespoke function-calling setup tied to one provider's API.&lt;br&gt;
With MCP, you'd expose a &lt;code&gt;search_inventory&lt;/code&gt; tool and an &lt;code&gt;inventory_item&lt;/code&gt; resource once. The model calls the tool when it needs live data, gets structured results back, and you're not rewriting integration logic every time you add a new AI feature to the product.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Where People Get Tripped Up Early On&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A few things caught me off guard the first time through, and they're worth knowing before you build anything a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;digital marketing in Ludhiana&lt;/a&gt; team might eventually depend on for reporting automation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tool descriptions matter more than you'd expect, since the model relies entirely on your description to decide when and how to call a tool. Vague descriptions produce vague tool usage.&lt;/li&gt;
&lt;li&gt;Error handling needs to be explicit in your return values, since the model can't infer a failure state from a thrown exception the way your app's error boundary would&lt;/li&gt;
&lt;li&gt;Local (stdio) vs remote (HTTP/SSE) transport changes your deployment story significantly, decide early which one your use case actually needs&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Should You Actually Use This Right Now&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you're building AI features that need to reach live data or perform actions beyond generating text, probably yes. If you're doing straightforward prompt-and-response work with no external tool calls, MCP is overhead you don't need yet. It's a solution to the integration fragmentation problem specifically, not a replacement for basic API design.&lt;/p&gt;

&lt;p&gt;Teams building this kind of AI feature work into client projects, the sort of thing a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;best SEO company in Ludhiana&lt;/a&gt; offering broader digital services increasingly gets asked about, tend to treat MCP as infrastructure worth learning now rather than waiting until it's unavoidable.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;A Quick Look at Resources, Not Just Tools&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Tools get most of the attention in MCP writeups, but resources are worth understanding too, since they cover the "give the model read access to data" side rather than the "let the model do something" side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server.resource(
  "project-tasks",
  "tasks://active",
  { description: "List of currently active project tasks" },
  async () =&amp;gt; {
    const tasks = await db.tasks.findActive();
    return {
      contents: [{
        uri: "tasks://active",
        mimeType: "application/json",
        text: JSON.stringify(tasks)
      }]
    };
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The distinction matters in practice. Tools are for actions and computed results; resources are for exposing data the model can read directly. Mixing the two up early on is a common source of confusion. I initially tried to model everything as a tool, including things that were really just read access to static data, and ended up with a server that was harder to reason about than it needed to be. Once resources and tools are separated cleanly, the rest of the implementation tends to fall into place faster, and it's the sort of infrastructure work a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;SEO services in Ludhiana&lt;/a&gt; provider building internal automation would want to set up correctly from the start rather than refactored later.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>mcp</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>JavaScript SEO: Common Rendering Pitfalls (Learned the Hard Way)</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Tue, 14 Jul 2026 11:31:07 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/javascript-seo-common-rendering-pitfalls-learned-the-hard-way-36kc</link>
      <guid>https://dev.to/mittal_technologies/javascript-seo-common-rendering-pitfalls-learned-the-hard-way-36kc</guid>
      <description>&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%2Fiq4bfke6y2bo07j09tq1.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%2Fiq4bfke6y2bo07j09tq1.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
I once spent three days convinced Google was just "being slow" to index a client's newly rebuilt React site. Turned out Googlebot was rendering the page just fine, it just wasn't seeing the same content a user's browser saw, because a chunk of it loaded via a client-side fetch call that fired after an intersection observer triggered on scroll. A crawler doesn't scroll. That was a dumb, expensive lesson, and I've since learned it's an extremely common one.&lt;/p&gt;

&lt;p&gt;JavaScript SEO isn't really about SEO knowledge at all. It's about understanding what actually happens between your framework rendering content and a crawler trying to index it, and that gap is where most rendering pitfalls live.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;The Core Problem, Explained Simply&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Search engines (and increasingly, AI crawlers) need to see your final rendered HTML, not just your initial server response, a distinction that a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;SEO services in Ludhiana&lt;/a&gt; provider running technical audits checks before anything else. If your content depends on client-side JavaScript execution to appear, you're trusting the crawler to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actually, execute your JS correctly&lt;/li&gt;
&lt;li&gt;Wait long enough for async operations to resolve&lt;/li&gt;
&lt;li&gt;Not hit a rendering budget limit before your content loads&lt;/li&gt;
&lt;li&gt;Handle whatever framework-specific quirks your app introduces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Googlebot generally handles this reasonably well now. A lot of other crawlers, including several AI bots, are far less reliable at it.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Pitfall 1: Content Behind User Interaction&lt;/strong&gt;
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This pattern is a crawler trap&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&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;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&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="nf"&gt;fetchContent&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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;If your important content only loads after a scroll event, a click, or a hover, most crawlers will never see it. I've caught this exact pattern hiding pricing tables, product descriptions, and entire FAQ sections from indexing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; load critical content on initial render and treat scroll-triggered loading as a progressive enhancement for below-the-fold, non-essential content only.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Pitfall 2: Relying Entirely on Client-Side Rendering&lt;/strong&gt;
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Client-only rendering leaves an empty shell for crawlers&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setData&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="nx"&gt;data&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;Content&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt; : &amp;lt;Loading /&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A crawler hitting this before the fetch resolves sees a loading spinner and not much else. Depending on rendering budget and timeout behavior, that might be all it ever indexes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server-side rendering (SSR) or static generation solves this at the source&lt;/li&gt;
&lt;li&gt;If a full SSR migration isn't realistic, at minimum pre-render your highest-value pages&lt;/li&gt;
&lt;li&gt;Frameworks like Next.js, Nuxt, and SvelteKit handle this natively, it's usually a configuration problem, not a rewrite&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Pitfall 3: Broken or Missing Canonical Signals in SPAs&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Single-page apps handling routing client-side sometimes fail to update canonical tags, meta descriptions, and title tags per route. I've seen sites where every single route reported the same title tag because the meta update logic only ran on initial load, not on client-side navigation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Easy to miss: update meta tags on route change, not just mount&lt;/span&gt;
&lt;span class="nf"&gt;useEffect&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pageTitle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;updateMetaTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageDescription&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;route&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// don't forget the dependency array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Pitfall 4: Infinite Scroll Without Paginated Fallbacks&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Infinite scroll feels great for users. For crawlers, content past the initial load is often invisible unless you're providing an alternative path to it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add paginated URLs alongside infinite scroll (?page=2, ?page=3) even if most users never see them&lt;/li&gt;
&lt;li&gt;Link those paginated URLs somewhere crawlable, a sitemap entry or a visible "load more" link with a real href&lt;/li&gt;
&lt;li&gt;Test with JS disabled to see what actually persists without client-side execution&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Pitfall 5: Render Blocking on Slow Third-Party Scripts&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If your critical content waits on a third-party script (a personalization engine, an A/B testing tool, a chat widget's data layer) to finish before rendering, you're at the mercy of that script's reliability and speed inside a crawler's rendering budget, which is typically far less generous than a real browser's.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audit what's actually blocking your main content render, not just what's blocking visual paint&lt;/li&gt;
&lt;li&gt;Move non-essential third-party scripts to load after critical content, not before&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How to Actually Test This&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use Google Search Console's URL Inspection tool and look at the rendered HTML, not just the fetched source&lt;/li&gt;
&lt;li&gt;Fetch your page with JavaScript disabled and manually diff it against what a real browser show&lt;/li&gt;
&lt;li&gt;Check server logs for crawler user-agents and confirm they're actually hitting your key routes, not just the homepage, the kind of check a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;digital marketing in Ludhiana&lt;/a&gt; team runs routinely as part of ongoing technical maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This kind of technical audit is exactly the layer a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;best digital marketing company in Ludhiana&lt;/a&gt; usually runs before touching content strategy at all, since no amount of content work fixes a page, a crawler can't actually see.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What I'd Prioritize First&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you're triaging a site with rendering issues and can't fix everything at once, this is the order I'd generally recommend, similar to how a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;best SEO company in Ludhiana&lt;/a&gt; would sequence a technical audit for a client under time pressure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fix content that's entirely hidden behind interaction first, since that's a hard zero for crawlability&lt;/li&gt;
&lt;li&gt;Move to SSR or pre-rendering for your highest-traffic-potential pages next&lt;/li&gt;
&lt;li&gt;Fix per-route meta tag updates, since this affects how every page gets represented in search results&lt;/li&gt;
&lt;li&gt;Address infinite scroll and third-party render blocking last, since these tend to be partial rather than total visibility losses&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A Quick Diagnostic Script&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you want to check any of this yourself without opening DevTools manually, here's a rough script for diffing rendered vs. raw HTML using Puppeteer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&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;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;compareRendering&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&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;rawHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&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;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderedHtml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Raw HTML length:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rawHtml&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rendered HTML length:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;renderedHtml&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Difference:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;renderedHtml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rawHtml&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="c1"&gt;// Anything wildly larger in rendered vs raw is content&lt;/span&gt;
  &lt;span class="c1"&gt;// that depends entirely on JS execution to appear&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;compareRendering&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://example.com/product-page&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A large gap between raw and rendered length is a decent early signal that meaningful content is JS-dependent. It's not a substitute for checking Search Console's actual rendered HTML, but it's a fast local sanity check before you go digging deeper, and it's often the first diagnostic a &lt;a href="https://mittaltechnologies.com/service/digitalmarketing" rel="noopener noreferrer"&gt;digital marketing agency Ludhiana&lt;/a&gt; runs before touching anything else on a client audit.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>seo</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>Lessons We Learned Building a Production Flutter App</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Thu, 09 Jul 2026 11:08:44 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/lessons-we-learned-building-a-production-flutter-app-4mn8</link>
      <guid>https://dev.to/mittal_technologies/lessons-we-learned-building-a-production-flutter-app-4mn8</guid>
      <description>&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%2Fwz3xf2z17lx3jchytfhu.jpeg" 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%2Fwz3xf2z17lx3jchytfhu.jpeg" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
We shipped a Flutter app to production about eight months ago for a client in the logistics space, and I want to write down the stuff that actually bit us, because most "Flutter in production" posts I read beforehand were either marketing fluff or way too basic. This one's going to be messier and more specific, closer to what actually happened.&lt;/p&gt;

&lt;p&gt;Quick context: cross-platform requirement (iOS + Android), tight timeline, a small team of three, and a backend already built in Node.js that we had to integrate with, not design from scratch. That combination shaped a lot of the decisions below. On the client side, this was a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website development company Ludhiana&lt;/a&gt;, led engagement, with our team handling the Flutter build specifically while the client's existing web presence stayed with their original team.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;State Management: We Switched Mid-Project and It Was Worth It&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We started with &lt;code&gt;Provider&lt;/code&gt; because it's what the docs push you toward early on, and honestly, it's fine for small apps. Once our widget tree got deeper and we had cross-screen state that needed to survive navigation (cart contents, auth state, a multi-step form), Provider started producing a lot of boilerplates and some annoying rebuild issues we couldn't cleanly debug.&lt;/p&gt;

&lt;p&gt;We migrated to Riverpod around week six. Painful in the short term, genuinely worth it by the end. The dependency injection is cleaner, testing providers in isolation is much easier, and we stopped fighting &lt;code&gt;context&lt;/code&gt;-based lookups breaking when widgets moved around in the tree.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;final cartProvider = StateNotifierProvider&amp;lt;CartNotifier, CartState&amp;gt;((ref) {
  return CartNotifier(ref.read(apiClientProvider));
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I were starting fresh today, I'd just start with Riverpod (or Bloc if your team prefers a stricter pattern) and skip Provider entirely, unless the app is genuinely tiny and staying that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Platform Channels Are Where the Pain Lives&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Flutter's "write once" promise holds up well for UI. It holds up much less well the moment you need something platform-specific, in our case, a barcode scanner integration and background location updates for delivery tracking. We ended up writing native platform channel code for both, and this is where the estimate blew past what we'd budgeted.&lt;/p&gt;

&lt;p&gt;The barcode scanner especially, we tried three different Flutter plugins before landing on a combination of a maintained plugin plus custom native fallback code for a specific Android device model our client's driver actually used in the field. Turns out that device had a known camera focus bug that only showed up in production, never in our testing on newer phones. Lesson: test on the actual hardware your users have, not whatever's sitting in your office.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static const platform = MethodChannel('com.example.app/scanner');

Future&amp;lt;String?&amp;gt; scanBarcode() async {
  try {
    final result = await platform.invokeMethod&amp;lt;String&amp;gt;('startScan');
    return result;
  } on PlatformException catch (e) {
    // handle scanner-specific failures here
    return null;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Build Times Got Bad, Then We Fixed Them&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;By month four, our CI build times had crept up to almost 20 minutes for a full iOS build, which killed our iteration speed. Most of it came from an over-bloated dependency list, we'd added packages for things we ended up building custom solutions for anyway and never removed the old dependencies. A dependency audit cut our pubspec down significantly and shaved several minutes off build time.&lt;/p&gt;

&lt;p&gt;We also moved to splitting our CI pipeline, so Android and iOS builds ran in parallel instead of sequentially, which sounds obvious in hindsight but wasn't how our pipeline was originally set up. Small process fix, meaningful time saved across a team running multiple builds a day. It's a fix I'd now recommend to any &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website developer in Ludhiana&lt;/a&gt; working on a mobile-and-web hybrid team, since the CI habits that work fine for a single web repo often don't scale cleanly once mobile builds get added into the same pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;API Integration and Error Handling Needed More Structure Than We Planned For&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is the unglamorous part nobody talks about enough. Our backend team (working separately, coordinated through a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;mobile app development in Ludhiana&lt;/a&gt; partnership on our side) had solid API docs, but real-world network conditions, spotty connectivity for delivery drivers moving between areas, exposed gaps in our error handling that our happy-path testing never caught.&lt;/p&gt;

&lt;p&gt;We ended up building a proper retry-with-backoff layer and a local queue for actions taken while offline, syncing once connectivity returned. Should have built this from day one instead of bolting it on in month five once drivers started reporting "lost" data that was actually just stuck in a failed request nobody retried.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Future&amp;lt;T&amp;gt; withRetry&amp;lt;T&amp;gt;(Future&amp;lt;T&amp;gt; Function() action, {int retries = 3}) async {
  for (int attempt = 0; attempt &amp;lt; retries; attempt++) {
    try {
      return await action();
    } catch (e) {
      if (attempt == retries - 1) rethrow;
      await Future.delayed(Duration(seconds: pow(2, attempt).toInt()));
    }
  }
  throw Exception('Retry logic failed unexpectedly');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;Testing Discipline Slipped Under Deadline Pressure, and We Paid For It&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We had decent widget test coverage early on. Then the deadline got tighter, and testing was the first thing that got quietly deprioritized, which is a classic mistake, and we knew it was a classic mistake even while doing it. Two production bugs that shipped in month six would have been caught by tests we didn't have time to write. Not catastrophic, but embarrassing, and it cost more time to hotfix than it would have taken to just write the tests properly the first time.&lt;/p&gt;

&lt;p&gt;If you're working with an &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;app developer Ludhiana&lt;/a&gt; team or any external partner on a Flutter project, it's worth setting a hard rule upfront about minimum test coverage for anything touching payments, auth, or offline sync, the categories where bugs are expensive rather than just annoying.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What I'd Actually Do Differently Next Time&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Start with Riverpod, not Provider. Budget real time for platform channel work if there's any hardware integration at all, don't assume a plugin will "just work" on every device. Set up parallel CI builds from day one. And don't let testing slip just because the deadline is tight; it always costs more time later than it saves now.&lt;/p&gt;

&lt;p&gt;Flutter's genuinely good for cross-platform work when the app is UI-heavy and doesn't need deep hardware integration. The moment hardware or background processes get involved, budget extra time and extra patience, because that's where the framework's abstractions start leaking.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>mobile</category>
      <category>architecture</category>
    </item>
    <item>
      <title>I Logged Every AI Suggestion During a Two-Week Client Project. These Were Actually Useful.</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Wed, 08 Jul 2026 06:36:41 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/i-logged-every-ai-suggestion-during-a-two-week-client-project-these-were-actually-useful-1nnj</link>
      <guid>https://dev.to/mittal_technologies/i-logged-every-ai-suggestion-during-a-two-week-client-project-these-were-actually-useful-1nnj</guid>
      <description>&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%2Fg0zs7f4au7r847y1ocv1.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%2Fg0zs7f4au7r847y1ocv1.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
I got tired of the extremes in this conversation. Half of Twitter acts like AI coding assistants are replacing developers by Friday. The other half insists it's all hallucinated garbage that wastes more time than it saves. So, on a two-week client project last month, I logged every AI suggestion I actually used, rejected, or modified, and the results were a lot more boring and more useful than either camp would have you believe.&lt;/p&gt;

&lt;p&gt;Context first: this was a mid-sized e-commerce platform rebuild, React frontend, Node backend, fairly standard stack. I kept a running note file next to my editor, tagging every suggestion as accepted-as-is, accepted-with-edits, or rejected, along with a one-line reason why. Not scientific, but honest.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;What Actually Turned Out Useful in Two Weeks of Real Work&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Boilerplate and repetitive patterns were where AI suggestions earned their keep, no contest. Writing the fifth nearly identical form validation schema of the week, the AI correctly guessed the pattern from the first four and saved genuine typing time. Same with test scaffolding, given an existing test file's structure, it reliably generated new test cases following the same conventions, which I then filled in with actual assertions. Not glamorous, but it added up to real hours saved across two weeks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// AI correctly inferred this pattern after seeing 3 similar schemas
const productSchema = z.object({
  name: z.string().min(1).max(200),
  price: z.number().positive(),
  sku: z.string().regex(/^[A-Z0-9-]+$/),
  inventory: z.number().int().nonnegative()
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was also genuinely good at explaining unfamiliar error messages and stack traces from libraries I didn't know well. One dependency threw a cryptic error about a circular reference during serialization, and instead of digging through GitHub issues for twenty minutes, I pasted the trace and got a plausible explanation in seconds, which turned out to be correct once I verified it against the library's source.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Where It Actively Wasted My Time&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Business logic specific to this client's inventory rules was where things fell apart. The platform had a genuinely unusual rule, certain product bundles needed inventory decremented from a shared pool, but only during specific promotional windows, with a fallback to individual inventory tracking otherwise. Every suggestion confidently implemented the common, generic version of bundle inventory logic, which was wrong for this client's actual business rule. It wasn't a bad suggestion in a vacuum. It was a wrong suggestion delivered with total confidence, which is a worse failure mode than an obviously broken suggestion, because it takes longer to notice the mistake.&lt;/p&gt;

&lt;p&gt;I also burned time on suggestions for the payment integration that looked plausible but referenced API methods that didn't exist in the version of the SDK we were actually using. This is the failure mode I trust least, code that reads perfectly reasonably, compiles-looking syntax, references a method name that sounds exactly like something that should exist, and simply isn't real. Caught it because I actually ran the code rather than trusting it on sight, which is the only defense against this particular problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Middle Ground: Accepted With Real Edits&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This was the largest category by volume, honestly. Suggestions that got the shape of the solution right but needed real correction, wrong edge case handling, missing null checks, or a reasonable approach that just didn't account for something specific to this codebase's existing patterns. A good chunk of the API route handlers fell here: the AI correctly guessed our error-handling middleware pattern from context but consistently missed one specific logging call our team always includes for audit purposes on write operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// AI got the structure right, missed our team's audit logging convention
async function updateInventory(req, res) {
  try {
    const result = await inventoryService.update(req.body);
    auditLog.record('inventory.update', req.user.id, result); // had to add this manually every time
    res.json(result);
  } catch (err) {
    next(err);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pattern, right shape, missing the project-specific convention, was the single most common thing I logged across the two weeks. It's not a knock against the tooling. It just means these tools are pattern-matching against general conventions, not your specific team's unwritten rules, and there's no shortcut around teaching it those rules through context or just doing the edit yourself.&lt;br&gt;
The same held true on the frontend side. Teams doing &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;web design in Ludhiana&lt;/a&gt; work know component structure often follows a designer's specific system, not a generic pattern, and AI suggestions for UI components consistently defaulted to the most common layout convention instead of the client's actual design tokens, which had to be corrected by hand every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What I'd Actually Tell a Client About This&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Clients ask us about this constantly now, usually some version of "should we worry about AI replacing our dev team, or should we be using it more aggressively." Working on client projects through &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;web development in Ludhiana&lt;/a&gt; engagements, my honest answer is that it's a genuinely useful multiplier on the boring 60% of a project, boilerplate, test scaffolding, explaining unfamiliar errors and actively risky on the specific 40% that makes a client's business unique. The two-week log basically confirmed that split numerically instead of just as a vibe.&lt;/p&gt;

&lt;p&gt;If your team is evaluating whether a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website development company Ludhiana&lt;/a&gt; business relies on using these tools responsibly, the actual question to ask isn't "do you use AI." It's "how do you catch the confidently wrong suggestions before they ship." That's the part that actually matters, and it's the part most teams don't have a real answer for yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Discipline That Actually Made This Useful&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;None of this worked well without actually running the code and reading it critically, every single time, no exceptions even for suggestions that looked obviously fine. The moment I got lazy about that, twice, that I caught, was exactly when a subtly wrong suggestion slipped through toward a commit before I caught it in review. Both times it was the "sounds right, isn't real" failure mode, not a logic error, which is genuinely the harder one to catch by just reading code.&lt;/p&gt;

&lt;p&gt;Any &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;web developer Ludhiana&lt;/a&gt; working with these tools daily should build this same habit, log it if you can, but at minimum, treat every suggestion as a draft from a very fast, very confident junior developer who's read a lot of code but doesn't know your specific codebase's history.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Final Tally&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Across two weeks: roughly a third accepted as-is, mostly boilerplate and scaffolding. A little under half accepted with real edits, mostly missing project-specific conventions. The rest rejected outright, split fairly evenly between wrong business logic and hallucinated API references. Nothing dramatic. Nothing that confirms extreme take you'll find on social media.&lt;/p&gt;

&lt;p&gt;If you're a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website developer in Ludhiana&lt;/a&gt; or anywhere else deciding how much to lean on these tools, the honest answer from two weeks of actually counting is: a lot, for the boring stuff, and cautiously, with your own eyes on every line, for anything that touches the specific reason a client hired you instead of a template. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>From Localhost to Production: A Developer's Website Hardening Checklist for 2026</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Tue, 07 Jul 2026 08:54:05 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/from-localhost-to-production-a-developers-website-hardening-checklist-for-2026-a7h</link>
      <guid>https://dev.to/mittal_technologies/from-localhost-to-production-a-developers-website-hardening-checklist-for-2026-a7h</guid>
      <description>&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%2F1dzdot0q4bkpffkrmapj.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%2F1dzdot0q4bkpffkrmapj.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;br&gt;
Every developer has that moment. You've been running the app on localhost for weeks, everything's fine, &lt;code&gt;.env&lt;/code&gt; files are loose, CORS is wide open because who cares, it's just you and your terminal. Then deploy day comes and suddenly all that comfortable looseness becomes a liability. This developer's website hardening checklist for 2026 is basically the list we run through every single time before flipping something from dev mode to production, because muscle memory alone isn't enough anymore.&lt;/p&gt;

&lt;p&gt;I say this as someone who's shipped that exact &lt;code&gt;.env&lt;/code&gt; file to a public repo before. Once. Never again but once was enough to build this checklist out properly instead of trusting memory.&lt;/p&gt;

&lt;p&gt;For context, this list comes out of running deploys for a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website development company Ludhiana&lt;/a&gt; clients hire specifically because we treat this stuff as process, not vibes. Doesn't matter how experienced the individual dev is, checklists catch what tired brains miss at 2 AM before a launch.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Environment Variables: The Boring Check That Saves You&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;First thing, always: grep your entire codebase for anything that looks like a secret before you even think about deploying.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grep -rn "API_KEY\|SECRET\|PASSWORD\|TOKEN" --include="*.js" --include="*.ts" .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This catches the obvious stuff, but the sneakier problem in 2026 is framework specific. If you're on Next.js, double-check that anything without the &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; prefix genuinely isn't referenced anywhere in client-side code. It's an easy mistake to make when you're refactoring fast and forget which file runs where.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// This leaks to the browser bundle if used client-side
const dbPassword = process.env.DB_PASSWORD; // fine in API routes, disaster in components

// This is the safe pattern for anything the client legitimately needs
const publicKey = process.env.NEXT_PUBLIC_API_KEY;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;CORS: Localhost Habits Don't Survive Production&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;On localhost, it's tempting to just slap &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt; on everything and move on with building features. That habit needs to die before deploy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Localhost comfort, production liability
app.use(cors({ origin: "*" }));

// What should actually ship
app.use(cors({
  origin: process.env.NODE_ENV === "production"
    ? ["https://yourdomain.com"]
    : ["http://localhost:3000"],
  credentials: true
}));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've seen this exact wildcard survive into production more times than I'd like to admit, usually because it got set during early testing and nobody circled back to tighten it. It's worth adding a pre-deploy grep for this pattern specifically.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Rate Limiting Isn't Optional Anymore&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This one didn't used to make every checklist, but with AI-driven scraping and credential stuffing attempts up significantly this year, it's non-negotiable now. Even a basic implementation goes a long way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import rateLimit from "express-rate-limit";

const limiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100,
  message: "Too many requests, slow down."
});

app.use("/api/", limiter);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pair this with stricter limits specifically on auth routes, since login endpoints are the most common target for automated attempts.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Database Query Safety: Still the Classic Mistake&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;SQL injection feels like a solved problem until you find it in a client's supposedly modern codebase. It usually hides in places nobody thought to check, like a search feature bolted on late in development.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Vulnerable, still shows up more than you'd expect
const query = `SELECT * FROM users WHERE email = '${userInput}'`;

// Parameterized, the way it should always be written
const query = "SELECT * FROM users WHERE email = $1";
db.query(query, [userInput]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're on an ORM like Prisma or Drizzle, this risk drops significantly by default, but raw queries still creep in during performance optimization work, so it's worth a manual scan before shipping. Honestly, this is the kind of thing any decent &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;web developer Ludhiana&lt;/a&gt; team should be scanning for reflexively, ORM or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Authentication Token Storage&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Storing JWTs in localStorage is convenient and also a genuinely bad idea for anything handling sensitive data. It's vulnerable to XSS in ways httpOnly cookies simply aren't.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Convenient but vulnerable to XSS token theft
localStorage.setItem("token", jwt);

// Safer approach
res.cookie("token", jwt, {
  httpOnly: true,
  secure: true,
  sameSite: "strict"
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I get why localStorage is tempting, it's simpler to work with in a lot of frontend setups. It's just not worth the tradeoff once real user data is involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Headers You're Probably Missing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Security headers are cheap to add and genuinely useful. This is one of those checks that takes five minutes and prevents entire categories of attack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.use((req, res, next) =&amp;gt; {
  res.setHeader("X-Content-Type-Options", "nosniff");
  res.setHeader("X-Frame-Options", "DENY");
  res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
  next();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're on Next.js, this can go in &lt;code&gt;next.config.js&lt;/code&gt; instead, which is cleaner and easier to maintain across the whole app. A clean, well-structured &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;web design in Ludhiana&lt;/a&gt; teams often build from scratch tends to make this kind of config easier to audit too, versus a messy patchwork of inherited templates and plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Dependency Audit Before Every Deploy&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Run this before every production push, not just occasionally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm audit --production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix what's flagged as high or critical severity at minimum. It's tedious, sure, but outdated dependencies are consistently one of the top causes of breaches we've had to clean up after the fact. This kind of gap is exactly what a proper &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;cybersecurity audit&lt;/a&gt; should catch if your own process misses it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Working With Someone Outside Your Own Codebase&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Even with a solid personal checklist, there's real value in bringing in outside review before anything customer-facing goes live. It's not about the code being bad, it's about blind spots that come from staring at the same codebase for weeks straight. A second review from a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;cyber security company in Ludhiana&lt;/a&gt; or wherever your team's based tends to catch the stuff you've genuinely stopped seeing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Final Thought&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;None of this is exciting work. It's grep commands and header configs and double-checking things you're pretty sure you already did right. But localhost forgives sloppiness in ways production never does, and the gap between "works on my machine" and "safe in production" is exactly where these checks live.&lt;br&gt;
Ship carefully. The bots are definitely watching your DNS propagate.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>I Ran a 2026 Security Audit on a Fresh WordPress + WooCommerce Site: Here's Everything That Broke</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Mon, 06 Jul 2026 09:22:26 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/i-ran-a-2026-security-audit-on-a-fresh-wordpress-woocommerce-site-heres-everything-that-broke-b90</link>
      <guid>https://dev.to/mittal_technologies/i-ran-a-2026-security-audit-on-a-fresh-wordpress-woocommerce-site-heres-everything-that-broke-b90</guid>
      <description>&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%2F1vochvrqd6e9dg4ri7cy.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%2F1vochvrqd6e9dg4ri7cy.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
I spun up a brand new WordPress install last week, added WooCommerce, a handful of the most commonly recommended plugins, and did basically nothing custom. No weird theme hacks, no sketchy nulled plugins, just a standard setup like thousands of small businesses launch every single day. Then I ran a full 2026 security audit on this fresh WordPress and WooCommerce site, expecting maybe two or three minor flags.&lt;br&gt;
I found eleven issues. On a site that was, by all appearances, doing everything "right."&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Setting Up the Baseline&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Stack was simple: WordPress core (latest version), WooCommerce, a popular free theme, and five plugins covering SEO, caching, contact forms, backups, and a security plugin because irony demanded it. Fresh install, no content beyond placeholder products, default settings left mostly untouched, basically the exact starting point a new client site looks like on day one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp core version
# 6.8.1

wp plugin list --status=active
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran the audit using a mix of manual checks and automated scanning, then verified anything the scanner flagged by hand before trusting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Issue 1: Default Login URL, No Rate Limiting&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The obvious one, but still worth stating plainly: &lt;code&gt;/wp-login.php&lt;/code&gt; was wide open with zero rate limiting on failed attempts. I simulated repeated login attempts and got no lockout, no CAPTCHA trigger, nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for i in {1..20}; do
  curl -s -X POST https://freshsite.test/wp-login.php \
    -d "log=admin&amp;amp;pwd=wrongpass$i" -o /dev/null -w "%{http_code}\n"
done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All twenty attempts returned 200. On a live site, that's an open invitation for credential stuffing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Issue 2: WooCommerce REST API Exposing More Than It Should&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This one surprised me more than it probably should have. The WooCommerce REST API, even without generated keys, leaked product and category data through publicly accessible endpoints that weren't properly scoped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl https://freshsite.test/wp-json/wc/store/v1/products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returned full product listings including internal SKUs, which isn't catastrophic on its own, but it's the kind of data leak that becomes ubiquitous across default WooCommerce installs simply because almost nobody checks REST API scope during setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Issue 3: XML-RPC Still Enabled by Default&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I genuinely thought this had mostly died out as a concern, but XML-RPC was active and responding, which still gets used for brute-force amplification attacks via &lt;code&gt;system.multicall&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -X POST https://freshsite.test/xmlrpc.php \
  -d '&amp;lt;?xml version="1.0"?&amp;gt;&amp;lt;methodCall&amp;gt;&amp;lt;methodName&amp;gt;system.listMethods&amp;lt;/methodName&amp;gt;&amp;lt;/methodCall&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Got a full method list back, no restrictions. Disabling this outright, unless something specifically depends on it, is basically free security.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Issue 4: File Permissions Looser Than They Should Be&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Default install left &lt;code&gt;wp-config.php&lt;/code&gt; at 644 permissions instead of the tighter 600 recommended for anything holding database credentials.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ls -la wp-config.php
# -rw-r--r-- 1 www-data www-data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not a critical flaw by itself, but combined with any other vulnerability giving read access to the filesystem, this turns into a much bigger problem fast. Small fix, meaningful risk reduction.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Issue 5 Through 8: The Plugin Pile-Up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is where things got genuinely uncomfortable. Two of the five plugins had unpatched vulnerabilities listed in public CVE databases from earlier in the year, both still showing as "up to date" according to the plugin's own version number, because the vulnerability was in a version range that technically wasn't the newest release yet, just recent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp plugin list --update=available
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is exactly the kind of gap a scheduled &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;cybersecurity audit&lt;/a&gt; catches that a one-time setup check never will, since plugin vulnerabilities get disclosed on an ongoing basis, not in a single batch.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Issue 9: Checkout Page Missing Additional Transport Security&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;SSL was active, which is table stakes at this point, but the checkout page wasn't setting HSTS headers, meaning a user's first visit over HTTP before any redirect could theoretically be intercepted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.use((req, res, next) =&amp;gt; {
  res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
  next();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WordPress doesn't handle this by default, it needs to be added at the server config level, which is easy to forget on a standard shared hosting setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Issue 10 and 11: Admin Account Sprawl and Weak Password Policy&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The install had two admin accounts left over from the setup process, one of which was a leftover default account nobody had bothered removing. Password policy was also essentially nonexistent, accepting anything over six characters with no complexity requirement.&lt;/p&gt;

&lt;p&gt;This is honestly the most common finding across every WordPress site I've personally audited, fresh or otherwise. Access control keeps losing to convenience, every single time, on every kind of project.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What This Means If You're Launching WooCommerce in 2026&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;None of these eleven issues were exotic. Nothing here required advanced exploitation skills, just patience and a checklist. That's honestly the scary part. A fresh, "default" WordPress and WooCommerce site, set up by someone following standard documentation, ships with real exposure baked in from day one.&lt;/p&gt;

&lt;p&gt;If you're running an online store, this is exactly the kind of gap that gets caught by pairing development with a proper &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;cyber security company in Ludhiana&lt;/a&gt; or equivalent, someone whose entire job is watching for exactly this stuff rather than treating it as a one-time launch checkbox. A &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website development company Ludhiana&lt;/a&gt; businesses actually trust for ongoing WooCommerce projects should be running audits like this as standard practice, not as an upsell.&lt;/p&gt;

&lt;p&gt;I'll be re-running this same audit on the same install again in three months just to see how much drifts back open on its own. My guess is at least half of it.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>woocommerce</category>
      <category>webdev</category>
      <category>cybersecurity</category>
    </item>
    <item>
      <title>How I Reduced API Response Times by 70% Without Changing My Database</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Thu, 02 Jul 2026 08:13:10 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/how-i-reduced-api-response-times-by-70-without-changing-my-database-56cl</link>
      <guid>https://dev.to/mittal_technologies/how-i-reduced-api-response-times-by-70-without-changing-my-database-56cl</guid>
      <description>&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%2Fzvc52bosjkv4wdhqueal.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%2Fzvc52bosjkv4wdhqueal.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
So, a few months back I got handed a legacy Node.js API that was averaging around 1.4 seconds per request on some of our heavier endpoints. Users were complaining, the frontend team was slapping loading spinners on everything to hide the pain, and the initial instinct from pretty much everyone, including me if I'm honest, was "we need to migrate the database." We didn't. Got the average down to around 420ms without touching the schema or swapping the database engine at all. Writing this up because I think a lot of teams jump to the expensive fix before ruling out the cheap ones.&lt;/p&gt;

&lt;p&gt;Quick context: this was an order management API for a mid-sized e-commerce client, built maybe four years ago, handed off between a couple of different dev teams over that time, the usual story. Nobody fully owned it anymore. That's often exactly the kind of codebase where these problems hide in plain sight.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Step one, actually profile instead of guessing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Everyone has a theory about what's slow before they've looked at any numbers. I did too, honestly, my first guess was the database. So, I stopped guessing and started measuring. Threw some &lt;code&gt;console.time&lt;/code&gt; blocks through the request lifecycle initially just to get a rough shape of where time was going, then moved to proper APM tracing once I had a hypothesis worth confirming.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.time('db-query');
const result = await db.query(sql, params);
console.timeEnd('db-query');

console.time('serialization');
const payload = serializeResponse(result);
console.timeEnd('serialization');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Turns out the database query itself was taking maybe 80-120ms. Not amazing, but nowhere near the villain everyone assumed it was. The real time sink was everywhere else, N+1 queries hiding inside a "single" endpoint call, redundant serialization work, and basically zero caching on data that barely changes minute to minute.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Fix 1: killed the N+1 queries with proper eager loading&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This was the biggest single win by a wide margin. The endpoint was fetching a list of orders, then looping through and firing off a separate query for each order's line items. Classic N+1, and with pagination set to 50 items per page, that's 51 sequential round trips to the database for what should've been one API call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// before - N+1 disaster
const orders = await Order.findAll();
for (const order of orders) {
  order.items = await OrderItem.findAll({ where: { orderId: order.id } });
}

// after - single query with join
const orders = await Order.findAll({
  include: [{ model: OrderItem, as: 'items' }]
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one change alone cut response time nearly in half on the heaviest endpoint. I know N+1 queries are a well-worn topic in performance write-ups, almost a cliché at this point, but I keep finding them in production codebases anyway, so clearly the lesson hasn't fully landed industry-wide.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Fix 2: added a caching layer for data that doesn't need to be real-time&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A decent chunk of the response payload was product metadata, stuff that updates maybe once a day if that. There was no good reason to hit the database for it on every single request.&lt;br&gt;
&lt;/p&gt;

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

async function getProductMetadata(productId) {
  const cached = await redis.get(`product:${productId}`);
  if (cached) return JSON.parse(cached);

  const data = await Product.findByPk(productId);
  await redis.set(`product:${productId}`, JSON.stringify(data), 'EX', CACHE_TTL);
  return data;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple, almost boringly so, but it removed a huge amount of repeated, unnecessary work from the hot path.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Fix 3: trimmed the response payload to what the frontend actually uses&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Turned out the API was returning full ORM objects with dozens of fields the frontend never touched, half of them internal flags nobody remembered adding. Serializing all of that, especially nested relations, was adding measurable overhead on every single request. Switched to explicit response DTOs instead of just dumping the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function toOrderResponse(order) {
  return {
    id: order.id,
    status: order.status,
    total: order.total,
    items: order.items.map(i =&amp;gt; ({ name: i.name, qty: i.qty, price: i.price }))
  };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Smaller payloads, faster serialization, faster network transfer on top of that. All three add up more than people expect, especially on mobile connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Fix 4: connection pooling had been misconfigured the whole time&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Found the pool size still sitting at the driver's default, way too low for our actual concurrent load. Requests were literally queuing for a free connection during traffic spikes, which doesn't show up clearly when you're profiling a single isolated request, only under real concurrent load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const pool = new Pool({
  max: 25, // was defaulting to 10
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the kind of thing that's easy to overlook because it hides behind the symptoms rather than causing an obvious error.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What I'd tell anyone facing a similar problem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Profile before you assume anything. Everyone's first instinct is to blame the database, and sometimes it genuinely is the culprit, but more often it's the code sitting around the query that's the real problem, redundant calls, missing caching, bloated payloads, connection handling nobody's revisited in years. A full database migration is expensive, risky, and honestly avoidable more often than teams think, especially when nobody's bothered to profile first.&lt;/p&gt;

&lt;p&gt;If you're maintaining an older system and running into similar complaints, it's worth getting a second, less attached set of eyes on it before committing to a rewrite. This kind of performance audit is something we do fairly often at &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;Mittal Technologies&lt;/a&gt;, working alongside teams that just need someone to actually trace the problem rather than guess at it. If there's a genuine architectural issue underneath, that's usually where a proper &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;software development company in Ludhiana&lt;/a&gt; earns its keep, digging into the parts nobody's had time to revisit.&lt;/p&gt;

&lt;p&gt;A lot of legacy codebases end up desultory in their architecture, not through any single bad decision, but through years of different hands touching the same system with no shared plan. That's usually where these performance issues quietly accumulate.&lt;/p&gt;

&lt;p&gt;If you're working on something similar and want a structured audit rather than a guess-and-check approach, teams doing &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;custom software development in Ludhiana&lt;/a&gt; tend to have this exact profiling-first workflow baked into how they approach legacy handoffs, which honestly saves a lot of wasted migration effort down the line.&lt;/p&gt;

&lt;p&gt;Curious if others have run into similar N+1 traps hiding inside seemingly simple endpoints, or connection pool defaults that nobody thought to touch. Feel free to drop your own war stories in the comments, I always enjoy hearing where these things hide in other people's codebases.&lt;/p&gt;

</description>
      <category>node</category>
      <category>performance</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>Why Developers Are Becoming the New IT Procurement Team Without Realizing It</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Tue, 30 Jun 2026 11:53:12 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/why-developers-are-becoming-the-new-it-procurement-team-without-realizing-it-54ko</link>
      <guid>https://dev.to/mittal_technologies/why-developers-are-becoming-the-new-it-procurement-team-without-realizing-it-54ko</guid>
      <description>&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%2Fi6mwpsqsmhomp3ro2yoz.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%2Fi6mwpsqsmhomp3ro2yoz.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
You ran &lt;code&gt;npm install&lt;/code&gt; this morning and added a package. Maybe a date utility. Maybe a logging library. Maybe something for handling webhooks. You evaluated it, decided it looked good, checked the GitHub stars and the latest release date, and added it to your dependencies.&lt;/p&gt;

&lt;p&gt;Congratulations. You just made a procurement decision.&lt;/p&gt;

&lt;p&gt;Not how you'd describe it, probably. Procurement sounds like something that happens in a conference room with someone from finance, a six-week evaluation cycle, and a purchase order with three levels of approval. But what you did, evaluated a vendor (the package author), assessed capabilities, accepted licensing terms, and integrated it into your organization's software supply chain, is functionally procurement. Just faster. And without the organizational oversight that procurement usually involves.&lt;/p&gt;

&lt;p&gt;This is happening everywhere, at every company that employs developers. And most IT departments and business leadership either don't fully grasp it or don't have a framework designed for it.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;How This Shift Happened (And Why the Old Model Broke Down)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ten years ago, if you needed third-party software, meaningful procurement friction was built into the process. Software meant a vendor relationship, a sales conversation, a license key, often an annual contract. That friction created some level of deliberate decision-making by default; you couldn't accidentally end up committed to a vendor because the process forced a conversation.&lt;/p&gt;

&lt;p&gt;Then several things happened in parallel. Open source became the default. Cloud APIs made external service integration trivially easy. Package managers made dependency management nearly frictionless. Container registries meant a fully functional development environment using software from a dozen different sources could be running in minutes.&lt;/p&gt;

&lt;p&gt;The friction disappeared. Developer velocity increased dramatically, which is mostly good. But the organizational oversight that friction had accidentally been enforcing? That didn't get replaced with anything intentional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "dependencies": {
    "stripe": "^12.0.0",
    "sendgrid": "^7.7.0",
    "openai": "^4.0.0",
    "aws-sdk": "^2.1400.0",
    "datadog-lambda-js": "^8.0.0",
    "sentry": "^7.0.0"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at a realistic &lt;code&gt;package.json&lt;/code&gt; for a production web application. Stripe is a payment processing vendor relationship. SendGrid is an email delivery provider. OpenAI is an AI API vendor. AWS is your cloud infrastructure. Datadog is your observability vendor. Sentry handles your error reporting. Every developer who added one of those made a "should we use this vendor and accept their terms?" decision. Whether it felt like that decision or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Specific Scenarios Where Developer-Led Procurement Is Happening&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure-as-Code as purchasing:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This Terraform config is also a purchasing decision
resource "aws_instance" "app_server" {
  ami           = "ami-0c55b159cbfafe1f0"
  instance_type = "c5.4xlarge"  # $0.68/hr on-demand without reserved instance
}

resource "aws_rds_instance" "primary_db" {
  instance_class  = "db.r5.2xlarge"  # $0.48/hr
  multi_az        = true
  engine_version  = "15.4"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a developer writes a Terraform config and applies it to production, they've made infrastructure purchasing decisions that would traditionally involve capacity planning, finance approval, and a conversation about reserved instance commitments versus on-demand pricing. Now it's a PR that gets reviewed for correctness, not for cost implications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker images as vendor selection:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:20-alpine    # Specific runtime choice with security implications
FROM postgres:16       # Database vendor choice
FROM nginx:stable-alpine  # Web server choice with specific update cadence
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every base image in your Dockerfile is a vendor relationship. The security posture, patch update frequency, and license terms of those images matter for production systems and in most organizations, the developer who wrote the Dockerfile made those choices without any formal evaluation process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third-party API integrations as data sharing decisions:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Adding this integration is also a data handling decision
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const response = await client.chat.completions.create({
  model: "gpt-4o",
  messages: [{ 
    role: "user", 
    content: userGeneratedContent  // What's actually in here at runtime?
  }]
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you build an AI API integration like this, you're implicitly making decisions about what data flows to that vendor's servers, what their retention policy means for your users' data, and what your application does when their service has an outage. Those are vendor relationship questions that used to get evaluated formally. Now they're embedded in code.&lt;/p&gt;

&lt;p&gt;Companies working with &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;IT support service in Ludhiana&lt;/a&gt; providers who understand modern development workflows are increasingly bringing these conversations into their security architecture discussions, not just network perimeter topics, but supply chain and integration governance too.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Three Risk Categories This Creates&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Supply chain security.&lt;/strong&gt; The npm ecosystem has had genuine incidents involving malicious packages, code that looks legitimate and has real functionality but also contains something you didn't want. When a developer installs a package without scrutiny of its provenance, they're potentially introducing untrusted code into production. This isn't paranoia; it's a documented and recurring problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Licensing exposure.&lt;/strong&gt; GPL, AGPL, MIT, Apache 2.0, commercial proprietary, these licenses have substantially different implications for how your software can be used, modified, and distributed. A developer who adds a GPL-licensed library to a commercial proprietary application without noticing has created a licensing compliance issue that Legal cares about significantly. Most developers aren't thinking about license compatibility when they run &lt;code&gt;npm install&lt;/code&gt;, and most companies don't have a process that catches it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data flow decisions embedded in code.&lt;/strong&gt; When application code sends data to a third-party API, someone makes a decision about where that data goes. Often, it's the developer who wrote the integration, who may not have had full visibility into what data would flow through at runtime, or what the vendor's data retention policies actually mean for user PII.&lt;/p&gt;

&lt;p&gt;For growing tech companies or for those working with a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website development company Ludhiana&lt;/a&gt; partner on client projects, having clear answers to "what third parties does our application send data to, and under what terms?" is increasingly a client expectation, not just an internal concern.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What the Developer-IT Gap Costs in Practice&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In a lot of engineering organizations, there's a genuine cultural friction between developer teams and IT or security functions. Developers see IT as the people who enforce policies that slow things down without understanding modern development. IT sees developers as people who create new attack surfaces without consulting anyone. Both views have enough truth in them to sustain the tension.&lt;/p&gt;

&lt;p&gt;This friction means these groups often don't talk until something breaks, a licensing audit, a security incident, a compliance question that requires answering "what third parties have access to customer data?" quickly and accurately.&lt;/p&gt;

&lt;p&gt;The gap is also present in product-adjacent work. Design systems and &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;UI UX designing in Ludhiana&lt;/a&gt; workflows involve a stack of design tools, prototype tools, user research platforms, and analytics integrations. Each of those represents a third-party relationship that often gets established informally, without any formal evaluation.&lt;/p&gt;

&lt;p&gt;Working with &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;managed IT services in Ludhiana&lt;/a&gt; that understand modern development practices, rather than ones still primarily focused on network infrastructure, gives engineering teams a partner who can help translate between development velocity and organizational governance needs. That translation layer is often what's missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What Procurement-Aware Development Actually Looks Like&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The goal isn't to burden developers with a formal approval process for every package. That's unworkable and just moves the behavior underground.&lt;/p&gt;

&lt;p&gt;What works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A lightweight dependency policy&lt;/strong&gt; with clear, checkable rules: no packages with zero maintenance activity in 12+ months; check license compatibility before adding anything with a copyleft license; verify download velocity for any package with under 100k weekly downloads; check for known vulnerabilities before adding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An approved integrations list for common external services.&lt;/strong&gt; If your team needs an observability platform, here are three that have been security-reviewed. If you need an email API, here are two with appropriate data handling terms. This removes the friction without removing the governance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated tooling in the pipeline:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .github/workflows/security.yml
- name: License check
  uses: actions/setup-node@v3
- run: npx license-checker --onlyAllow 'MIT;Apache-2.0;BSD-2-Clause;ISC'

- name: Dependency audit
  run: npm audit --audit-level=moderate

- name: Supply chain check
  uses: socket-security/socket-security-action@v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Embedding governance in the CI/CD pipeline means the checks happen automatically, without requiring developer discipline on every commit. The governance is just part of the build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quarterly architecture reviews&lt;/strong&gt; that look at the third-party integration map for a product. What external services does our application communicate with? What data flows to each? Has anything changed since last quarter? This isn't a sprint ceremony; it's a lightweight periodic check that catches drift before it becomes a real problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Mindset Shift&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The developer who runs &lt;code&gt;npm install&lt;/code&gt; isn't doing anything wrong. The mental model that needs to change is organizational, recognizing that technical decisions made by developers carry business implications beyond the technical domain, and building lightweight systems to surface and manage those implications without becoming bureaucratic drag on engineering velocity.&lt;/p&gt;

&lt;p&gt;The alternative, pretending developers aren't making procurement decisions because nobody officially designated them as procurement managers, just means those decisions happen ungoverned. Which is how organizations accumulate supply chain vulnerabilities, licensing issues, and data handling gaps that nobody planned for.&lt;/p&gt;

&lt;p&gt;You're already doing procurement. You might as well do it intentionally. &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>cybersecurity</category>
      <category>programming</category>
    </item>
    <item>
      <title>From CRUD Apps to AI Agents: How Software Development Is Changing in 2026</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Mon, 29 Jun 2026 12:20:13 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/from-crud-apps-to-ai-agents-how-software-development-is-changing-in-2026-2al0</link>
      <guid>https://dev.to/mittal_technologies/from-crud-apps-to-ai-agents-how-software-development-is-changing-in-2026-2al0</guid>
      <description>&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%2F3y2czels2yt40lliqjwu.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%2F3y2czels2yt40lliqjwu.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Remember when "full stack developer" mostly meant you could wire up a database, build some REST endpoints, and slap a frontend on top? How software development is changing in 2026 basically boils down to this: we've moved from building apps that just store and retrieve data to building systems that reason, plan, and take action on their own. And honestly, a lot of us are still catching up to what that actually means in practice.&lt;/p&gt;

&lt;p&gt;I want to walk through this shift the way I've experienced it firsthand, not as some abstract trend piece, but as someone who's actually had to refactor a "boring" CRUD backend into something that supports agentic behavior and learned a few things the hard way along the process. A lot of this comes out of real hands-on work with the &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;best AI Solutions company in Ludhiana&lt;/a&gt; handling client projects that wanted agentic features without scrapping their existing backend entirely.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;CRUD Was Never the Hard Part; State Management for Agents Is&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A standard CRUD app is conceptually simple. User submits data, you validate it, you write it to a database, you read it back later. Even with some complexity around relationships and permissions, the mental model stays fairly linear.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// classic CRUD endpoint, nothing fancy
app.post('/tasks', async (req, res) =&amp;gt; {
  const task = await db.tasks.create({
    title: req.body.title,
    userId: req.user.id,
    status: 'pending'
  });
  res.json(task);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An AI agent breaks that linear model immediately. The agent doesn't just write a record, it decides whether writing a record is even the right action, sometimes calling multiple tools in sequence, sometimes backtracking when a tool call fails, sometimes asking a clarifying question before doing anything at all. Suddenly your simple "create" endpoint needs to support a much messier execution path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// agentic endpoint, a different beast entirely
app.post('/agent/task', async (req, res) =&amp;gt; {
  const plan = await agent.plan(req.body.userIntent);
  const results = [];
  for (const step of plan.steps) {
    const result = await agent.executeStep(step, { context: results });
    results.push(result);
    if (result.requiresClarification) {
      return res.json({ status: 'needs_input', question: result.question });
    }
  }
  res.json({ status: 'complete', results });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That loop, with branching, partial state, and the possibility of pausing mid-execution to wait on human input, is the actual architectural shift everyone's grappling with right now, and it's a lot harder to test cleanly than a basic CRUD route ever was. Unit tests that worked fine for deterministic CRUD logic don't translate cleanly here either, you end up writing tests around probabilistic outcomes and acceptable ranges of behavior rather than exact expected output, which took some genuine mental adjustment on my part.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Tool Calling Is the New API Design Skill&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A few years back, designing a good REST API meant thinking carefully about resource naming, status codes, and pagination patterns. Now half my design time goes into writing tool definitions an agent can reliably call without misusing them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const tools = [
  {
    name: "search_inventory",
    description: "Search product inventory by name or SKU. Returns stock count and price.",
    parameters: {
      type: "object",
      properties: {
        query: { type: "string", description: "product name or SKU" }
      },
      required: ["query"]
    }
  }
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The description field above matters way more than it looks like it should. Vague descriptions lead to agents calling the wrong tool at the wrong time or passing malformed parameters because the model genuinely didn't understand what the tool expects. This has turned into its own discipline, somewhere between technical writing and API design, and I underestimated how much time it'd actually eat up when I started building agentic features into client projects involving &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;web development in Ludhiana&lt;/a&gt; work that originally had nothing to do with AI at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Memory and Context Are Now First-Class Architecture Concerns&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In a CRUD app, "memory" was just whatever's in the database. Simple. With AI agents, you've got a layered memory problem, short-term conversational context, longer-term user preference memory, and sometimes a vector store holding semantically searchable history. Getting this layering right took me a few false starts before it actually worked smoothly across sessions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getAgentContext(userId, currentMessage) {
  const recentHistory = await getRecentMessages(userId, 10);
  const relevantMemories = await vectorStore.search(currentMessage, { userId, limit=5 });
  return { recentHistory, relevantMemories };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get this wrong and your agent either forgets everything between sessions, which feels broken to users, or it drags in irrelevant context that confuses its responses. There's a real balancing act here, and frankly most teams I've talked to are still tuning this through trial and error rather than following some established best practice, because the established best practices are still being written in real time. Most of the production-grade &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website development services in Ludhiana&lt;/a&gt; work I've seen lately involves exactly this kind of trial-and-error memory tuning baked into the project timeline from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Error Handling Looks Completely Different with Agents&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;CRUD error handling was predictable: validation failed, return 400, database down, return 500. With agents, failures are fuzzier. A tool call might technically succeed but return data the agent misinterprets. The model might hallucinate a function call that doesn't exist. You need defensive layers that didn't exist in traditional backend work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function safeToolCall(toolName, params) {
  if (!availableTools[toolName]) {
    return { error: `Unknown tool: ${toolName}`, recoverable: true };
  }
  try {
    return await availableTools[toolName](params);
  } catch (err) {
    logToolFailure(toolName, params, err);
    return { error: err.message, recoverable: isRecoverable(err) };
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;recoverable&lt;/code&gt; flag matters a lot in practice. Some failures should let the agent retry with adjusted parameters; others should just stop the chain and surface the issue to a human. Figuring out which is which, for your specific domain, takes actual iteration, not just copying a pattern from a blog post (including, slightly ironically, this one).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Testing Strategies That Actually Hold Up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One area I haven't seen discussed enough is how testing itself needs to evolve. Traditional integration tests assume deterministic output, you call an endpoint, you assert on an exact response shape. Agentic systems resist that pattern because the same input can legitimately produce slightly different but equally valid output paths depending on how the model interprets a prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('agent successfully books a task without crashing', async () =&amp;gt; {
  const result = await agent.plan("schedule a follow-up call next week");
  expect(result.steps.length).toBeGreaterThan(0);
  expect(result.steps.some(s =&amp;gt; s.tool === 'create_calendar_event')).toBe(true);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the test checks for behavior patterns rather than exact output, did it call the right category of tool, did it produce a non-empty plan, rather than asserting on a specific string. This shift in testing philosophy alone took longer to adjust to than most of the actual coding work, and it's worth budgeting real time for it rather than assuming your existing test suite philosophy will transfer cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Wrapping Up&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The shift from CRUD to agentic systems doesn't make traditional backend skills useless, honestly the fundamentals of clean data modeling and solid API design matter just as much as ever, they're just the foundation now rather than the whole job. What's changed is everything layered on top: planning, tool orchestration, memory management, and a much fuzzier relationship with failure states than we're used to from purely deterministic systems.&lt;/p&gt;

&lt;p&gt;There's also a documentation gap worth mentioning here. Most teams document their REST endpoints reasonably well at this point, it's a solved problem with established conventions. Agentic systems don't have that same maturity yet. Tool descriptions, planning logic, and the reasoning behind why certain tasks get delegated to an agent versus handled deterministically all tend to live in someone's head rather than in any written reference, which becomes a real liability the moment that person moves to a different project or leaves the team entirely.&lt;/p&gt;

&lt;p&gt;If you're starting to build agentic features into an existing product, my honest advice is don't rewrite everything at once. Pick one well-defined workflow, wrap it in agent behavior, and learn from how it breaks before expanding further. If you'd rather have an experienced team handle this transition, this is the exact kind of migration work a solid &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website development company in Ludhiana&lt;/a&gt; team is increasingly being hired for these days. It breaks in ways you won't fully predict ahead of time, and that's fine, that's basically the whole learning curve right now for everyone working in this space.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>backend</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>10 PWA Features Every Front-End Developer Should Implement in 2026</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Thu, 25 Jun 2026 11:56:42 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/10-pwa-features-every-front-end-developer-should-implement-in-2026-1ian</link>
      <guid>https://dev.to/mittal_technologies/10-pwa-features-every-front-end-developer-should-implement-in-2026-1ian</guid>
      <description>&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%2F8zooakkhwgaubfie1pgo.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%2F8zooakkhwgaubfie1pgo.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
Look, I've reviewed enough "PWA checklists" online that just list the same five buzzwords (offline support, push notifications, installable, blah blah) without any actual implementation detail. That's not useful to anyone trying to ship something real. So, here's the list I'd actually hand to a front-end dev joining a PWA project in 2026, with enough specificity that you can go implement it this week instead of just nodding along.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;1. A Properly Scoped Service Worker, Not A Copy-Pasted One&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is where most PWAs go wrong on day one. People copy a service worker boilerplate from a tutorial and never revisit the caching strategy for their actual use case. Don't do that. Decide deliberately between cache-first, network-first, and stale-while-revalidate per route, not globally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// stale-while-revalidate for product pages — fast, but stays fresh&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/product/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products-v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&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;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&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="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;fetchPromise&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Get this wrong and you either serve stale data forever or you lose the speed benefit entirely. There's no universal "right" strategy, it depends on how often the underlying data changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. A Web App Manifest That Actually Matches Your Brand&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The manifest.json file is easy to half-finish and forget about. Make sure &lt;code&gt;display: "standalone"&lt;/code&gt; is set if you want that real app feel without browser chrome, and don't skip the &lt;code&gt;maskable&lt;/code&gt; icon purpose, since Android adaptive icons will crop your logo awkwardly without it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your App Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"short_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"App"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"standalone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#0f172a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"theme_color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#0f172a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icon-512.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"512x512"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"image/png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"purpose"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"maskable"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;3. Background Sync For Form Submissions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If someone fills out a form, hits submit, and their connection drops mid-request, don't just show an error and make them redo everything. Background Sync queues the request and fires it automatically once connectivity returns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;submitOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SyncManager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;saveToIndexedDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;registration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sync-orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This single feature has saved more than one client of ours a wave of frustrated support tickets after we shipped it for a checkout flow. If you're working on anything resembling &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;ecommerce web app development in Ludhiana&lt;/a&gt; where dropped connections during checkout directly cost revenue, this is genuinely one of the highest-ROI features on this whole list, and it's surprising how often it gets skipped in favor of flashier additions.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. App Shortcuts For Faster Navigation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Long-press your app icon on Android and you can surface shortcuts straight to key actions, like "New Order" or "Track Package," without opening the app first and navigating manually. Underused feature, genuinely useful for retention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"shortcuts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Track Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/track"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"icons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/icon-track.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"96x96"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;strong&gt;5. Periodic Background Sync For Content Freshness&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Different from regular Background Sync, this one lets your PWA refresh cached content on a schedule even when it's not open, so users open a news or content app and see fresh data immediately rather than a loading spinner. Support is still inconsistent across browsers in 2026, so feature-detect before relying on it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. Web Push Notifications With Actual Targeting Logic&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Don't just blast every subscriber the same notification. Segment based on behavior, abandoned cart, restock alert, price drop, and keep the payload lean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;push&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;registration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/icon-192.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;targetUrl&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;h2&gt;
  
  
  &lt;strong&gt;7. File Handling API For Productivity-Style PWAs&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you're building anything where users work with files, documents, images, the File Handling API lets your PWA register as a handler so files can open directly into your app from the OS file explorer, much closer to native behavior than people expect from a web app.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;8. Adaptive Loading Based On Network Conditions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Use the Network Information API to detect connection quality and adjust what you load accordingly. Don't serve full-resolution hero images to someone on a throttled 3G connection in a low-signal area.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&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;slow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;effectiveType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2g&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;saveData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;loadImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slow&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;low-res&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high-res&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This matters a lot more for projects targeting markets with inconsistent mobile network quality than tutorials written for fast-wifi demo environments tend to acknowledge. Teams handling &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;mobile app development in Ludhiana&lt;/a&gt; for clients with users spread across tier-2 and tier-3 cities run into this constantly, and adaptive loading is usually one of the first optimizations that visibly moves the needle on real-world load complaints.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;9. Proper Update Notification Flow&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Don't let your service worker silently update in the background and leave users on a stale cached version indefinitely. Detect the new version and prompt the user or apply it gracefully on next navigation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;controllerchange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;showToast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;App updated. Refresh to see the latest version.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;I've seen production bugs linger for weeks because nobody implemented this properly and users were stuck on a cached, broken version with no way to know an update existed.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;10. Lighthouse-Driven Performance Budgets, Not Just A One-Time Audit&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Running Lighthouse once at launch and calling it done is a mistake I still see constantly. Set up CI-integrated performance budgets so a regression in your PWA score fails the build, not just shows up as a surprise three months later when someone finally remembers to check.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Bonus: How To Actually Test This Stuff Properly&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A quick note before the closing thoughts, because half the features above are useless if you're only testing them on your own machine with perfect office wifi and a service worker that's never been forced to handle a real cache invalidation edge case.&lt;/p&gt;

&lt;p&gt;Use Chrome DevTools' Application tab to manually trigger service worker updates, simulate offline mode, and inspect what's actually sitting in your cache storage versus what you assumed was there. Don't trust your mental model of the cache, verify it directly, because caching bugs are notoriously the kind of thing that work fine in dev and break silently three weeks into production.&lt;/p&gt;

&lt;p&gt;For network throttling, don't just use the generic "Slow 3G" preset and call it done. Test against the specific conditions your actual user base experiences, if you've got analytics on connection types, replicate the worst common scenario, not an arbitrary one DevTools ships with by default.&lt;/p&gt;

&lt;p&gt;And for push notifications specifically, test the full round-trip end to end, including what happens when a user has denied permission previously, revoked it later, or is using a browser that doesn't fully support the API yet. Edge cases here are where most production push implementations quietly fail without anyone noticing until support tickets start piling up.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Closing Thoughts From Someone Who's Shipped A Few Of These&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;None of these features are hard individually. What's hard is implementing all ten thoughtfully instead of cargo-culting a tutorial's service worker and calling the PWA "done." If you're working on something more business-critical than a side project and want a second set of eyes on the architecture, teams running &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;progressive web app in Ludhiana&lt;/a&gt; builds professionally have usually hit most of these edge cases already, which can save you a few weeks of debugging caching weirdness in production.&lt;/p&gt;

&lt;p&gt;And if you'd rather hand off the whole build instead of piecing it together yourself, a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;web &amp;amp; app development company Ludhiana&lt;/a&gt; with PWA experience can take this list and just implement it properly the first time, edge cases included.&lt;/p&gt;

&lt;p&gt;That's the list. Go implement one feature this week instead of bookmarking this and forgetting about it, that's usually how these things actually get shipped.&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>pwa</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>From Figma AI to Frontend Code: How Developers Can Speed Up Product Delivery with AI</title>
      <dc:creator>Mittal Technologies</dc:creator>
      <pubDate>Wed, 24 Jun 2026 10:50:08 +0000</pubDate>
      <link>https://dev.to/mittal_technologies/from-figma-ai-to-frontend-code-how-developers-can-speed-up-product-delivery-with-ai-59gj</link>
      <guid>https://dev.to/mittal_technologies/from-figma-ai-to-frontend-code-how-developers-can-speed-up-product-delivery-with-ai-59gj</guid>
      <description>&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%2F7oit19dvo5p5ioxqvbb4.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%2F7oit19dvo5p5ioxqvbb4.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Quick honest confession before we get into it: I used to roll my eyes a little when designers talked about "AI generating code from Figma." As a dev, I'd heard that promise before in different forms and it usually meant fifteen minutes of cleanup for every five minutes saved. But the workflow connecting Figma AI to actual frontend code has gotten noticeably less painful lately, and I think it's worth a proper, unglamorous look at what's actually working versus what's still mostly marketing.&lt;/p&gt;

&lt;p&gt;This isn't a "AI will replace frontend devs" post. It's the opposite, honestly, it's about where AI genuinely removes friction from product delivery, and where you still need to just sit down and write the code yourself like a normal person.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;What's Actually Changed in the Figma-to-Code Pipeline&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A couple of years ago, Figma-to-code tools produced markup that nobody wanted to touch, div soup, inline styles everywhere, zero semantic structure. It technically rendered the design, but maintaining it was its own kind of punishment.&lt;/p&gt;

&lt;p&gt;The current generation is meaningfully better at recognizing component patterns. If a design file has a properly structured component (a button variant, a card, a form input with defined states), the generated code is far more likely to come out as something resembling a real component instead of a one-off styled div. That's not magic, it's just better pattern recognition trained on cleaner design system inputs, but the practical difference shows up immediately once you're the one reviewing the pull request.&lt;/p&gt;

&lt;p&gt;Here's roughly what a generated button component looks like now versus what it used to look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// what we used to get
&amp;lt;div style={{padding: '12px 24px', background: '#1a73e8', borderRadius: '8px'}}&amp;gt;
  &amp;lt;span style={{color: 'white', fontSize: '14px'}}&amp;gt;Submit&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;

// what we're getting more often now
function Button({ variant = 'primary', children, ...props }) {
  return (
    &amp;lt;button className={`btn btn-${variant}`} {...props}&amp;gt;
      {children}
    &amp;lt;/button&amp;gt;
  );
}

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

&lt;/div&gt;



&lt;p&gt;Still needs review. Still needs adjustment for your actual codebase conventions. But it's a real starting point now instead of disposable scaffolding you delete the moment you open the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Where This Actually Saves Time on Real Projects&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The honest time savings show up mostly in two places: initial scaffolding and design-token syncing. When a designer hands off a new feature with a clean Figma file, generating a rough first-pass component structure straight from that file beats starting from a blank component every time. You're not writing from scratch, you're editing and correcting, which is a meaningfully different (and faster) mental task.&lt;/p&gt;

&lt;p&gt;Design tokens, colors, spacing, typography scales, syncing automatically from Figma into your CSS variables or Tailwind config also removes a genuinely annoying manual step that used to cause drift between design and code constantly. Someone updates a brand color in Figma, forgets to tell anyone, and three weeks later you've got two slightly different shades of blue living in production. AI-assisted token syncing catches a decent chunk of that drift automatically now, which, as someone who's debugged exactly that kind of inconsistency at 11pm before a launch, I genuinely appreciate.&lt;/p&gt;

&lt;p&gt;If you're working with a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website development company Ludhiana&lt;/a&gt; teams partner with for client builds, this kind of automated token pipeline is becoming a pretty standard task in project scoping now, not a nice-to-have add-on anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Where It Still Falls Apart&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Complex interactive states are still rough. Anything involving conditional rendering logic, complex form validation, or non-trivial state management gets generated in a way that's structurally close but functionally incomplete almost every time. You'll get a component that looks right and breaks the moment you try to wire up real logic behind it.&lt;/p&gt;

&lt;p&gt;Accessibility also needs a manual pass, every single time, no exceptions. Generated markup sometimes includes basic ARIA attributes, sometimes doesn't, and it's inconsistent enough that you can't trust it blindly. Treat AI-generated accessibility attributes as a draft to verify, never as a finished implementation, screen reader testing still needs a human running through it.&lt;/p&gt;

&lt;p&gt;And honestly, anything with genuinely custom interaction patterns, a non-standard carousel, a weird drag-and-drop flow, anything outside common component patterns, the AI tools mostly guess, and the guess is often wrong in ways that take longer to debug than just writing it manually would've taken in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A Practical Workflow That's Actually Working for Teams Right Now&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;What's working well on the projects I've seen lately looks roughly like this: designers build clean, well-named components in Figma (this part matters more than people realize, messy Figma files produce messy, generated code, garbage in garbage out applies hard here). Generate a first-pass code scaffold straight from that file. Dev reviews and rewrites the parts that need real logic, leaving the layout and styling mostly intact since that part usually translates cleanly. Run accessibility checks manually regardless of what the generation tool claims to have handled.&lt;/p&gt;

&lt;p&gt;Teams working with a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;UI UX design company in Ludhiana&lt;/a&gt; building client products are increasingly structuring their Figma files specifically with this downstream code generation in mind, proper component naming, consistent auto-layout usage, defined variants, because it directly affects how usable the generated code ends up being on the dev side. It's a small process shift on the design side that pays off noticeably on the engineering side.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Should You Actually Adopt This Workflow?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If your team already has decent Figma file hygiene, components, variants, consistent naming, yes, worth piloting on a low-stakes feature first. If your Figma files are a mess of ungrouped layers and one-off styles (no judgment, we've all inherited a file like that), fix that first. The AI tooling amplifies whatever discipline already exists in your design process; it doesn't create discipline that wasn't there.&lt;/p&gt;

&lt;p&gt;A &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;UI UX designing in Ludhiana&lt;/a&gt; studio I talked to recently put it well: AI didn't change their process so much as it made the cost of a sloppy process more visible, faster. Which, depending on how you look at it, might be the most useful thing it's done for their team, not the code generation itself, but the forcing function toward cleaner design files in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A Real Bug This Workflow Caused (and Taught Me Something)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Quick war story, because I think it's a useful one. On a recent project, a generated component for a pricing card looked perfect visually but had a subtle bug, the "most popular" badge was hard coded as visible on every card instead of conditionally rendering based on a prop. Visually it passed every design review because, well, it looked exactly like the Figma file, which only showed one card with the badge active.&lt;/p&gt;

&lt;p&gt;Took about twenty minutes to catch in QA, and another ten to fix. Not a disaster, but a useful reminder: AI-generated code matches what it sees, not what you actually need logically. The design file showed a static state. The code needed dynamic behavior. Those are two different problems, and the tool only solved the first one convincingly.&lt;/p&gt;

&lt;p&gt;That gap, visual fidelity versus functional correctness, is probably the single most important thing to internalize before leaning on this workflow for anything beyond simple presentational components. Treat every piece of generated logic as unverified until you've actually traced through the conditional paths yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How This Affects Project Timelines and Client Expectations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One thing worth flagging if you're managing client work: don't let this tooling make its way into your timeline estimates as a flat percentage cut across the board. The time savings are real but lumpy, heavy on scaffolding and styling, light to nonexistent on logic-heavy features. A project that's 80% complex interactions won't see nearly the speed up of one that's 80% straightforward presentational UI.&lt;/p&gt;

&lt;p&gt;I've seen teams promise faster delivery across an entire project based on early wins with simple components, then get squeezed when the harder, logic-heavy screens hit the same optimistic timeline. Worth scoping feature-by-feature instead of assuming a blanket multiplier, however tempting that pitch sounds in a client meeting.&lt;/p&gt;

&lt;p&gt;For agencies and freelancers pricing out work alongside a &lt;a href="https://mittaltechnologies.com/" rel="noopener noreferrer"&gt;website designing company in Ludhiana&lt;/a&gt; might recommend or partner with, it's probably smarter to quote conservatively on the logic-heavy chunks and let the AI-assisted savings show up as a pleasant surprise on the simpler screens, rather than baking unproven speedups into the original quote and hoping they materialize evenly across the whole build.&lt;/p&gt;

&lt;p&gt;Anyway, that's where things stand from where I'm sitting. Not revolutionary, but a genuinely useful addition to the toolbox if your design files are clean enough to feed it properly. Curious if others are seeing the same pattern, or if your stack's handling the logic-heavy stuff better than what I've run into.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>frontend</category>
      <category>code</category>
      <category>figma</category>
    </item>
  </channel>
</rss>
