<?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: Akhil Baraik</title>
    <description>The latest articles on DEV Community by Akhil Baraik (@akhilbaraik).</description>
    <link>https://dev.to/akhilbaraik</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%2F4172605%2F869f70fe-db44-4981-a07a-ef8d643e2fdd.jpg</url>
      <title>DEV Community: Akhil Baraik</title>
      <link>https://dev.to/akhilbaraik</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/akhilbaraik"/>
    <language>en</language>
    <item>
      <title>Redesigning Your Website Without Losing Your Google Traffic</title>
      <dc:creator>Akhil Baraik</dc:creator>
      <pubDate>Mon, 02 Nov 2026 09:49:20 +0000</pubDate>
      <link>https://dev.to/akhilbaraik/redesigning-your-website-without-losing-your-google-traffic-42g</link>
      <guid>https://dev.to/akhilbaraik/redesigning-your-website-without-losing-your-google-traffic-42g</guid>
      <description>&lt;p&gt;A new website feels like a fresh start. The design is cleaner, the pages load faster and the copy finally says what the business does. Then, a few weeks after launch, enquiries slow down and nobody can explain why.&lt;/p&gt;

&lt;p&gt;More often than not, the cause isn't the new design. It's what got lost on the way: old page addresses that now lead nowhere, titles that were rewritten without thought, and content that quietly disappeared. Google had spent years learning what the old site was about, and the redesign threw much of that away overnight.&lt;/p&gt;

&lt;p&gt;The good news is that this is avoidable. Here's the checklist I go through with clients before, during and after a redesign.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Take a snapshot of the old site before anything changes
&lt;/h2&gt;

&lt;p&gt;Before a single page is rebuilt, make a record of what you have today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A full list of page URLs. Your sitemap, a crawler tool or Google Search Console can give you this.&lt;/li&gt;
&lt;li&gt;Which pages bring in visitors and enquiries. Search Console's Performance report shows the pages and search terms that get clicks.&lt;/li&gt;
&lt;li&gt;Each important page's title, meta description and main heading.&lt;/li&gt;
&lt;li&gt;Pages that other websites link to, since those links carry weight you don't want to lose.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Save this somewhere the whole team can see. It becomes the reference for every decision that follows, and it's very hard to rebuild once the old site is gone.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Keep the URLs that already work, where you can
&lt;/h2&gt;

&lt;p&gt;The simplest way to protect rankings is to not change addresses that don't need changing. If &lt;code&gt;/services/web-design&lt;/code&gt; is already ranking, there's rarely a good reason to rename it to &lt;code&gt;/what-we-do/design&lt;/code&gt; just because the new menu is laid out differently.&lt;/p&gt;

&lt;p&gt;When a URL truly has to change, because pages are being merged, renamed or reorganised, that's fine. It just needs a redirect.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Map every old URL to a new home with 301 redirects
&lt;/h2&gt;

&lt;p&gt;A 301 redirect tells browsers and search engines that a page has moved permanently and where it went. Without it, anyone who clicks an old link, from Google, a directory or a bookmark, lands on a "page not found" error.&lt;/p&gt;

&lt;p&gt;Build a simple two-column sheet: old URL on the left, new URL on the right. A few rules help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Point each old page to the closest matching new page, not to the homepage. Sending everything to the homepage confuses visitors and search engines alike.&lt;/li&gt;
&lt;li&gt;If two old pages are being merged, redirect both to the merged page.&lt;/li&gt;
&lt;li&gt;Only let a page return "not found" if it genuinely has no replacement and no value.&lt;/li&gt;
&lt;li&gt;Avoid chains, where page A redirects to B, which redirects to C. Point A straight to C.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Don't drop content that was doing a job
&lt;/h2&gt;

&lt;p&gt;Redesigns often come with a push to "simplify", which can mean cutting long service pages down to a few lines. Those longer pages may be exactly why you ranked. They answered the questions people were searching for.&lt;/p&gt;

&lt;p&gt;Shorter can be better for reading, but check your snapshot first. If a page was bringing in traffic, keep the substance of what it covered, even if you reorganise or rewrite it. Thin pages with a headline and two sentences rarely rank for anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Carry over titles, headings and on-page basics
&lt;/h2&gt;

&lt;p&gt;For each page that's performing, compare the old and new versions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the title tag still clear and specific, and does it still mention the service and the place?&lt;/li&gt;
&lt;li&gt;Does the page still have one clear main heading?&lt;/li&gt;
&lt;li&gt;Are images given sensible file names and alt text?&lt;/li&gt;
&lt;li&gt;Are internal links still pointing to the right pages, not to old addresses?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is glamorous, but small slips here add up across a whole site.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Test on a staging site, and keep it hidden
&lt;/h2&gt;

&lt;p&gt;Build and review the new site on a staging address first. Just make sure search engines can't index it, usually with a password or a "noindex" setting, or Google may find two copies of your site.&lt;/p&gt;

&lt;p&gt;Then, and this is the step people forget, remove that block when the new site goes live. A live site still carrying a "noindex" tag or a blocking robots.txt file can vanish from search results surprisingly quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Launch day: check the essentials
&lt;/h2&gt;

&lt;p&gt;On the day you switch over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test a sample of old URLs from your sheet and confirm each one redirects to the right new page.&lt;/li&gt;
&lt;li&gt;Make sure the live site isn't set to "noindex" and robots.txt isn't blocking it.&lt;/li&gt;
&lt;li&gt;Submit the new sitemap in Google Search Console.&lt;/li&gt;
&lt;li&gt;Check that forms, phone links and tracking still work, since a broken contact form costs you enquiries straight away.&lt;/li&gt;
&lt;li&gt;Update the website link on your Google Business Profile and main listings if any addresses changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. Watch closely for the first few weeks
&lt;/h2&gt;

&lt;p&gt;Some movement in rankings after a redesign is normal while Google re-crawls the site. What you're looking for is anything that doesn't settle.&lt;/p&gt;

&lt;p&gt;For the first month or so, check Search Console every week for new "not found" errors and pages that have dropped out of the index. Fix missing redirects as you find them. Compare clicks on your key pages against your snapshot. If an important page has slipped, look at what changed on it: content, title, internal links or speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;A redesign should make your website better for visitors without throwing away the trust it has already built with search engines. Keep a record of the old site, keep URLs that work, redirect the ones that change, protect the content that ranks, and watch the numbers after launch.&lt;/p&gt;

&lt;p&gt;At Crework Tech, we're an IT and digital marketing agency in Ranchi with 7+ years of building websites and software and a 90% client retention rate, and this checklist is part of every rebuild we take on. If you're planning a redesign and want a second pair of eyes on the move, our &lt;a href="https://creworktech.in/seo-services-in-ranchi" rel="noopener noreferrer"&gt;SEO services in Ranchi&lt;/a&gt; cover exactly this kind of migration check.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>beginners</category>
      <category>smallbusiness</category>
    </item>
    <item>
      <title>Your First Business App: What to Build First and What to Leave Out</title>
      <dc:creator>Akhil Baraik</dc:creator>
      <pubDate>Thu, 29 Oct 2026 05:19:36 +0000</pubDate>
      <link>https://dev.to/akhilbaraik/your-first-business-app-what-to-build-first-and-what-to-leave-out-3ojf</link>
      <guid>https://dev.to/akhilbaraik/your-first-business-app-what-to-build-first-and-what-to-leave-out-3ojf</guid>
      <description>&lt;p&gt;When a business owner tells me they want an app, the first list of features is usually long. Online ordering, loyalty points, chat, a referral scheme, push offers, a delivery tracker, an admin dashboard, maybe a wallet. Every item makes sense on its own. Put together, they turn a three-month project into a year-long one, and the app launches late, over budget and harder to use than it needs to be.&lt;/p&gt;

&lt;p&gt;A better first version does one job well and leaves room to grow. Here's how I suggest deciding what goes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Write down the one job the app must do
&lt;/h2&gt;

&lt;p&gt;Start with a single sentence: "A customer opens the app to ___." For a restaurant it might be "to order food for pickup or delivery." For a clinic, "to book an appointment." For a field-service company, "so our technicians can log each visit from the site."&lt;/p&gt;

&lt;p&gt;If you can't finish that sentence in a few words, the app isn't ready to be built yet. Everything in the first version should support that one job. Anything that doesn't can wait.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Check whether you need an app at all
&lt;/h2&gt;

&lt;p&gt;This sounds odd coming from a developer, but it saves people real money. A mobile-friendly website can handle a lot: menus, bookings, enquiry forms, even payments. People already have a browser, so there's nothing to download.&lt;/p&gt;

&lt;p&gt;An app starts to earn its place when customers come back often, when you need phone features like the camera, GPS, offline use or push notifications, or when your own staff will use it every day. If none of those apply, a good website may be the smarter first step, and you can add an app later.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Sort every feature into three lists
&lt;/h2&gt;

&lt;p&gt;Take your full wish list and split it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Must have.&lt;/strong&gt; Without it, the one job can't be done. For a food-ordering app, that's the menu, the cart, checkout, payment and an order confirmation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Should have soon.&lt;/strong&gt; Useful, but customers can live without it for a few months. Order history, saved addresses and reorder buttons often land here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nice to have.&lt;/strong&gt; Loyalty points, referral codes, in-app chat, themes. Good ideas, but they belong in a later version once you know people use the app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be strict with the first list. A feature that "would be good to have from day one" is usually a should-have in disguise.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Don't forget the parts customers never see
&lt;/h2&gt;

&lt;p&gt;Owners plan the customer screens and forget the back office. Someone has to add products, change prices, see new orders, mark them done and handle refunds. If there's no simple admin panel, your team ends up calling the developer for every small change.&lt;/p&gt;

&lt;p&gt;Plan these early too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Notifications to staff&lt;/strong&gt;, so a new order or booking isn't missed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic reports&lt;/strong&gt;, even just daily orders and revenue, so you can see whether the app is working.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who can do what&lt;/strong&gt;, if more than one person will manage it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Choose Android, iPhone or both with your customers in mind
&lt;/h2&gt;

&lt;p&gt;Look at who your customers actually are. In much of India, most customers are on Android, so some businesses start there. If your customers are spread across both, a cross-platform framework such as Flutter or React Native lets one codebase run on Android and iPhone, which usually keeps the first version's cost and timeline lower than building two separate native apps.&lt;/p&gt;

&lt;p&gt;Ask your developer which approach they recommend for your case and why. A good answer will mention your users and features, not just the tool they happen to prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Plan for life after launch
&lt;/h2&gt;

&lt;p&gt;An app isn't finished when it reaches the Play Store. Android and iOS release updates every year, payment gateways change their requirements, and real customers will find bugs that testing didn't. Budget for maintenance from the start and agree in writing who handles fixes and updates.&lt;/p&gt;

&lt;p&gt;Also make sure you own what you're paying for. Your developer account on Google Play and the App Store, the source code and the server should be in your business's name, or handed over to you if you ask.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Launch small, then listen
&lt;/h2&gt;

&lt;p&gt;Once the first version is live, watch what people actually do. Which screens do they use? Where do they drop off? What do they ask your staff about? That tells you which items from the "should have" and "nice to have" lists are worth building next, and which ones nobody misses.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple checklist before you talk to a developer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;One sentence describing the app's main job&lt;/li&gt;
&lt;li&gt;A clear reason it needs to be an app rather than a website&lt;/li&gt;
&lt;li&gt;Your feature list split into must-have, should-have and nice-to-have&lt;/li&gt;
&lt;li&gt;A note on what your staff need in the admin panel&lt;/li&gt;
&lt;li&gt;Which platforms your customers use&lt;/li&gt;
&lt;li&gt;A maintenance budget and an ownership agreement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bring that to your first meeting and you'll get more accurate quotes, fewer surprises and an app that does its main job well from day one.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Akhil Baraik is the founder of Crework Tech, an IT and digital marketing agency in Ranchi, India, with 7+ years of experience building software, websites and apps. If you're planning a first app, you can see how we approach &lt;a href="https://creworktech.in/mobile-app-development-in-ranchi" rel="noopener noreferrer"&gt;mobile app development in Ranchi&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>startup</category>
      <category>mobile</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>A Social Media Plan a Small Local Business Can Actually Keep Up With</title>
      <dc:creator>Akhil Baraik</dc:creator>
      <pubDate>Mon, 26 Oct 2026 06:35:56 +0000</pubDate>
      <link>https://dev.to/akhilbaraik/a-social-media-plan-a-small-local-business-can-actually-keep-up-with-5ea3</link>
      <guid>https://dev.to/akhilbaraik/a-social-media-plan-a-small-local-business-can-actually-keep-up-with-5ea3</guid>
      <description>&lt;p&gt;Most small-business owners I talk to have the same social media story. They open an Instagram and Facebook page, post every day for two weeks, run out of ideas, and then the page sits untouched for months. Anyone who finds it later sees a last post from last Diwali and wonders whether the business is still open.&lt;/p&gt;

&lt;p&gt;The problem is rarely effort. It's a plan that was too ambitious to begin with. Here's the simpler approach I suggest to local businesses, whether that's a clinic, a coaching centre, a restaurant or a shop.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Pick one or two platforms, not five
&lt;/h2&gt;

&lt;p&gt;You don't need to be everywhere. Ask where your customers already spend time and where they'd actually look you up.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A restaurant, salon, boutique or gym usually does well on Instagram, with Facebook alongside for an older audience.&lt;/li&gt;
&lt;li&gt;A B2B service, such as an accountant, consultant or software company, usually gets more from LinkedIn.&lt;/li&gt;
&lt;li&gt;Almost every local business should treat its Google Business Profile as a social channel too. Posts, photos and replies to reviews there are seen by people who are already searching for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two channels you keep alive beat five you abandon.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Set a rhythm you can hold for a year
&lt;/h2&gt;

&lt;p&gt;Before deciding how often to post, decide how much time you can honestly give it each week. If the answer is two hours, plan for two or three posts a week, not one a day. Consistency matters more than volume, because a page that posts steadily looks active and trustworthy, while a burst followed by silence looks the opposite.&lt;/p&gt;

&lt;p&gt;Block a fixed slot in your week, say Monday morning, to plan and schedule the next week's posts in one sitting. Meta Business Suite lets you schedule Facebook and Instagram posts for free, and LinkedIn lets you schedule posts natively, so you don't have to remember to post on the day.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use a few repeatable content types
&lt;/h2&gt;

&lt;p&gt;Running out of ideas is the main reason pages go quiet. The fix is to stop thinking of each post as a fresh idea and rotate a handful of formats instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Show the work.&lt;/strong&gt; A finished order, a before-and-after, a new arrival, a project you just delivered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Answer a question.&lt;/strong&gt; Take a question customers ask you on calls or WhatsApp and answer it in a short post or video.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behind the scenes.&lt;/strong&gt; Your team, your kitchen, your workshop, how something gets made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proof from customers.&lt;/strong&gt; A review (with permission), a thank-you message, a photo a customer tagged you in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Useful local information.&lt;/strong&gt; Opening hours over a festival, a new branch, parking tips, delivery areas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With five formats, a week's plan becomes "which two or three of these do I have material for?" rather than "what on earth do I post?"&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Make every post easy to act on
&lt;/h2&gt;

&lt;p&gt;A post that gets likes but no enquiries isn't doing much for a local business. Make sure people can take the next step without hunting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep your phone number, WhatsApp link, address and opening hours correct in every profile bio.&lt;/li&gt;
&lt;li&gt;Use the same business name, phone number and address everywhere, including your website and Google Business Profile. Mismatched details confuse customers and search engines alike.&lt;/li&gt;
&lt;li&gt;Add a clear line where it fits, like "Call to book", "Order on WhatsApp" or "Visit us in Lalpur", instead of leaving people to work it out.&lt;/li&gt;
&lt;li&gt;Link to the most relevant page on your website, not just the home page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Reply quickly, publicly and politely
&lt;/h2&gt;

&lt;p&gt;Comments and messages are where social media actually turns into business. Check them daily, even when you aren't posting. Reply to questions in the comments so the next person sees the answer. Thank people for good reviews, and answer bad ones calmly with an offer to sort it out offline. How you handle a complaint in public often says more about you than any post you make.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Spend on ads only after the basics work
&lt;/h2&gt;

&lt;p&gt;Boosting posts can help, but only once your page looks active, your profiles are complete and you know which posts people respond to. Start small, target the area you actually serve, and send people to a page or WhatsApp chat where they can enquire. If an ad isn't bringing enquiries, pause it and look at the offer and the landing page before spending more.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Check what's working once a month
&lt;/h2&gt;

&lt;p&gt;You don't need a complicated dashboard. Once a month, look at the built-in insights on each platform and ask three questions: which posts got the most saves, shares, comments or messages, which brought actual enquiries, and which fell flat. Do more of the first two kinds and less of the last. Ask new customers how they found you, too, because that tells you more than reach numbers do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it boring and steady
&lt;/h2&gt;

&lt;p&gt;A good social media presence for a local business isn't about going viral. It's about looking open, trustworthy and easy to contact every time someone checks. A small plan you follow every week will do far more for you than an ambitious one you drop after a fortnight.&lt;/p&gt;

&lt;p&gt;If you'd like help setting up a plan like this, or running it for you, my team at Crework Tech offers &lt;a href="https://creworktech.in/social-media-marketing-in-ranchi" rel="noopener noreferrer"&gt;social media marketing in Ranchi&lt;/a&gt; for local businesses. We're an IT and digital marketing agency, and we've been building websites, software and marketing for small businesses for over seven years.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Akhil Baraik is the founder of Crework Tech, an IT and digital marketing agency in Ranchi, Jharkhand.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>smallbusiness</category>
      <category>socialmedia</category>
      <category>marketing</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How a Small Business Can Get Mentioned in AI Search Answers (Without Chasing Tricks)</title>
      <dc:creator>Akhil Baraik</dc:creator>
      <pubDate>Thu, 22 Oct 2026 04:51:37 +0000</pubDate>
      <link>https://dev.to/akhilbaraik/how-a-small-business-can-get-mentioned-in-ai-search-answers-without-chasing-tricks-48i7</link>
      <guid>https://dev.to/akhilbaraik/how-a-small-business-can-get-mentioned-in-ai-search-answers-without-chasing-tricks-48i7</guid>
      <description>&lt;p&gt;More of the people I talk to now start a search by typing a full question into ChatGPT, Perplexity, Gemini or Copilot instead of Google. "Which IT company in Ranchi builds mobile apps?" "Who does website maintenance near me?" The tool doesn't hand back ten blue links. It writes one answer, and your business is either in it or it isn't.&lt;/p&gt;

&lt;p&gt;This is what people mean by AI search optimization, sometimes called GEO (generative engine optimization). It sounds new, but most of the work is the same groundwork good SEO always needed, done more carefully. Here's how I explain it to small-business owners.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Understand what these tools actually read
&lt;/h2&gt;

&lt;p&gt;AI assistants build answers from two places: what they learned from large crawls of the web during training, and, for many of them, live web search at the moment you ask. Either way, they're reading public pages. If the facts about your business are thin, scattered or contradictory online, the tool has nothing solid to repeat.&lt;/p&gt;

&lt;p&gt;So the first question isn't "how do I trick the AI?" It's "if a careful reader looked me up across the web, would they get a clear, consistent picture?"&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Say plainly what you do, where, and for whom
&lt;/h2&gt;

&lt;p&gt;AI answers lean on pages that state things clearly. Vague taglines like "we deliver excellence" give a model nothing to quote. A sentence like "We build custom web and mobile apps for small businesses in Ranchi, Jharkhand" does.&lt;/p&gt;

&lt;p&gt;On your own site, check that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your home page and each service page say what the service is, who it's for and where you work, in normal sentences near the top.&lt;/li&gt;
&lt;li&gt;Every service has its own page, rather than five services crammed into one paragraph.&lt;/li&gt;
&lt;li&gt;Your about page states when you started, what you specialise in and how to contact you.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Answer real questions on your pages
&lt;/h2&gt;

&lt;p&gt;People ask AI tools questions, so pages that answer questions directly are easy to draw from. Write down the questions customers actually ask you on calls and WhatsApp, such as how long a website takes, what a maintenance plan includes, or whether you work with businesses outside your city. Then answer them honestly on the relevant page, with a short FAQ section if it fits.&lt;/p&gt;

&lt;p&gt;Keep each answer self-contained. A two- or three-sentence answer that makes sense on its own is much more likely to be picked up than a point buried in a long paragraph.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Add structured data
&lt;/h2&gt;

&lt;p&gt;Schema markup is a small block of code that tells machines what a page is about: your business name, address, phone, services, and FAQs. Search engines have used it for years, and it helps AI-driven search read your pages without guessing. At minimum, most local businesses should have LocalBusiness (or Organization) markup and FAQPage markup where they have FAQs. Test it with Google's Rich Results Test after adding it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Keep your details identical everywhere
&lt;/h2&gt;

&lt;p&gt;Your name, address, phone number and description should match across your website, Google Business Profile, directories, social profiles and anywhere else you're listed. If one listing has an old number and another spells the business name differently, you're making it harder for any system, human or AI, to be sure they're all the same business.&lt;/p&gt;

&lt;p&gt;This is boring work, but it's one of the most useful things a local business can do.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Get mentioned on other sites, not just your own
&lt;/h2&gt;

&lt;p&gt;AI answers tend to reflect what the wider web says, not only what you say about yourself. Genuine mentions help: a listing in a relevant industry directory, a local news feature, a guest article on a site in your field, a customer review on a third-party platform, a helpful answer in a community forum. The goal is to be cited as a real, active business by sources other than your own website.&lt;/p&gt;

&lt;p&gt;Avoid shortcuts like bulk link packages or spun content. They don't build trust with readers, and they can hurt your regular search rankings.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Don't let the technical basics slip
&lt;/h2&gt;

&lt;p&gt;None of the above helps if crawlers can't read your site. Make sure pages load reasonably fast, aren't blocked by robots.txt by mistake, return proper status codes instead of errors, and have correct canonical tags and titles. Broken pages and duplicate content confuse both search engines and AI tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Check what the tools say about you
&lt;/h2&gt;

&lt;p&gt;Every month or so, ask a few AI assistants the questions your customers would ask, like "best website developers in [your city]" or "who offers [your service] in [your area]", and note whether you appear and whether the details are right. If something is wrong, trace it back: usually an outdated listing or an unclear page is the source. Results vary from tool to tool and day to day, so look for patterns rather than reacting to one answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest summary
&lt;/h2&gt;

&lt;p&gt;There's no switch that makes ChatGPT recommend you. What works is being clear about who you are, answering your customers' real questions, keeping your details consistent, and earning genuine mentions across the web. The nice side effect is that all of it improves your regular Google visibility too.&lt;/p&gt;

&lt;p&gt;At Crework Tech, an IT and digital marketing agency in Ranchi, we've been building websites and software for over seven years, and lately a lot of our conversations with clients are about this shift. If you want to go deeper, we've put together a guide to &lt;a href="https://creworktech.in/ai-search-optimization-services" rel="noopener noreferrer"&gt;AI search optimization and GEO&lt;/a&gt; that covers how tools like ChatGPT and Perplexity find websites.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Akhil Baraik is the founder of Crework Tech, an IT and digital marketing company in Ranchi, Jharkhand.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>seo</category>
      <category>ai</category>
      <category>smallbusiness</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What to Put in Place Before You Turn On Google Ads</title>
      <dc:creator>Akhil Baraik</dc:creator>
      <pubDate>Mon, 19 Oct 2026 04:55:54 +0000</pubDate>
      <link>https://dev.to/akhilbaraik/what-to-put-in-place-before-you-turn-on-google-ads-4p65</link>
      <guid>https://dev.to/akhilbaraik/what-to-put-in-place-before-you-turn-on-google-ads-4p65</guid>
      <description>&lt;p&gt;I run an IT and digital marketing agency in Ranchi. When a small business tells me Google Ads "did not work," the ads are rarely the first problem. The usual pattern is that the budget went live before the basics underneath it were ready: no clear offer, a weak landing page, no way to count a real lead, and no plan for what happens after someone clicks.&lt;/p&gt;

&lt;p&gt;Google Ads can send people to you the same day you switch it on. That is useful. It also means every gap in your setup burns money faster than SEO ever would. Here is the checklist I walk through with clients before we spend a rupee on clicks.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Know what a "lead" means for your business
&lt;/h2&gt;

&lt;p&gt;Write one sentence that defines a useful enquiry. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A WhatsApp message that includes the service they want and a phone number we can call back&lt;/li&gt;
&lt;li&gt;A form submit with name, phone and preferred time&lt;/li&gt;
&lt;li&gt;A booked consultation slot&lt;/li&gt;
&lt;li&gt;A call that lasts more than 30 seconds and is about a service you actually sell&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;"Website traffic" and "likes" are not leads. If you cannot count the outcome you care about, you cannot tell whether the ads are helping. Agree on that definition with whoever answers the phone or WhatsApp before the campaign starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Pick one offer and one primary location
&lt;/h2&gt;

&lt;p&gt;Ads work best when the promise is specific. "Best digital solutions in Jharkhand" is hard for a searcher to trust. "Commercial AC repair across Ranchi, same-day visit slots" is easier to click and easier to measure.&lt;/p&gt;

&lt;p&gt;Choose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The main service or product you want to sell this month&lt;/li&gt;
&lt;li&gt;The geography that matters (Ranchi only, Ranchi plus nearby towns, or a radius around your shop)&lt;/li&gt;
&lt;li&gt;The language people actually type into Google (often a mix of English and Hindi transliteration)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can add more services later. A first campaign with one clear offer teaches you more than a sprawling account with ten ad groups and thin traffic in each.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Send clicks to a page that matches the ad
&lt;/h2&gt;

&lt;p&gt;The landing page does not have to be fancy. It does have to match what the ad promised, load quickly on a phone, and make the next step obvious.&lt;/p&gt;

&lt;p&gt;Before you turn ads on, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The headline on the page repeats the service and city from the ad&lt;/li&gt;
&lt;li&gt;The main button or form is visible without scrolling on a phone&lt;/li&gt;
&lt;li&gt;Phone and WhatsApp are clickable&lt;/li&gt;
&lt;li&gt;There are no competing pop-ups that hide the form&lt;/li&gt;
&lt;li&gt;The page does not ask for more fields than you need to call the person back&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your homepage talks about everything you do, build a simple dedicated page for the campaign instead. Sending "AC repair Ranchi" traffic to a generic homepage is one of the fastest ways to waste spend.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Install conversion tracking properly
&lt;/h2&gt;

&lt;p&gt;Without tracking, you are guessing. At minimum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Google Analytics 4 on the site&lt;/li&gt;
&lt;li&gt;Google Ads conversion actions for the events that match your lead definition (form submit, click-to-call, WhatsApp click if you can measure it)&lt;/li&gt;
&lt;li&gt;A way to mark which enquiries came from ads when someone calls or messages (a unique phone number, a form field, or a simple "How did you find us?" habit on WhatsApp)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test a conversion yourself before launch. Submit a dummy form, confirm it appears in Ads and Analytics, then delete the test lead. Do not discover on day five that nothing was recording.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Make sure someone can answer within minutes
&lt;/h2&gt;

&lt;p&gt;Paid search traffic is impatient. If your ad runs from 9am to 7pm and nobody replies on WhatsApp until the next morning, you paid for clicks that cooled off.&lt;/p&gt;

&lt;p&gt;Decide who owns replies during ad hours, what a good first reply looks like, and how you hand a lead to sales or the technician. A short reply template is enough: thank them, confirm the service area, ask one clarifying question, and offer the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Set a learning budget and a review date
&lt;/h2&gt;

&lt;p&gt;You do not need a huge budget to learn. You do need enough clicks for Google to show patterns, and a fixed date when you will look at the numbers with a clear head.&lt;/p&gt;

&lt;p&gt;Write down before launch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily or monthly spend cap you can afford to treat as a test&lt;/li&gt;
&lt;li&gt;The locations and search themes that are in scope&lt;/li&gt;
&lt;li&gt;What you will pause if cost per useful lead stays too high after two weeks of clean data&lt;/li&gt;
&lt;li&gt;What "good enough to continue" looks like in your own numbers, not a blog post's averages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ignore vanity metrics for that review. Clicks and impressions matter only as the path to the lead definition you wrote in step one.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Separate brand searches from discovery searches
&lt;/h2&gt;

&lt;p&gt;If people already search for your business name, protect that with a small brand campaign so competitors cannot sit on it cheaply. Keep non-brand campaigns (service + city keywords) in their own structure so you can see what new demand actually costs.&lt;/p&gt;

&lt;p&gt;That split also stops you from celebrating "cheap leads" that were going to call you anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this fits together
&lt;/h2&gt;

&lt;p&gt;Ads amplify whatever system you already have. A clear offer, a matching page, working tracking and a human who answers will make a modest budget useful. Missing any one of those will make a large budget look like proof that "ads don't work."&lt;/p&gt;

&lt;p&gt;If you want a plain overview of how we structure Google Ads, Meta Ads and tracking for Ranchi businesses, we describe the approach on our &lt;a href="https://creworktech.in/digital-marketing-agency-in-ranchi" rel="noopener noreferrer"&gt;digital marketing agency in Ranchi&lt;/a&gt; page. Use it as a reference for the setup questions a team should ask you, not as a substitute for finishing this checklist yourself.&lt;/p&gt;

&lt;p&gt;Turn the ads on only after the checklist is done. The clicks will wait a day. Fixing a broken funnel after the money is gone is much harder.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Bio:&lt;/strong&gt; Akhil Baraik is the founder of Crework Tech, an IT and digital marketing agency in Ranchi, India. The firm has 7+ years of experience and a 90% client retention rate, and builds websites, web apps, mobile apps and custom software alongside SEO and digital marketing.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>marketing</category>
      <category>seo</category>
      <category>googleads</category>
    </item>
    <item>
      <title>What to Put in a Website Brief Before You Hire a Developer</title>
      <dc:creator>Akhil Baraik</dc:creator>
      <pubDate>Thu, 15 Oct 2026 04:41:17 +0000</pubDate>
      <link>https://dev.to/akhilbaraik/what-to-put-in-a-website-brief-before-you-hire-a-developer-38eg</link>
      <guid>https://dev.to/akhilbaraik/what-to-put-in-a-website-brief-before-you-hire-a-developer-38eg</guid>
      <description>&lt;p&gt;I run an IT and digital marketing agency in Ranchi. Most of the website projects that go wrong for small businesses do not go wrong in the code. They go wrong in the first week, when nobody wrote down what the site was supposed to do.&lt;/p&gt;

&lt;p&gt;A website brief is that write-up. It does not need to be long or technical. One or two pages in plain language is enough. It is the document you share with every agency or freelancer so they quote on the same thing, and so you have something to check against when the first design comes back.&lt;/p&gt;

&lt;p&gt;Here is what I ask clients to put in it before we start.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. What the business does, in one paragraph
&lt;/h2&gt;

&lt;p&gt;Say who you serve and what you sell or offer. Mention the city or area if that matters for your customers. Skip slogans. "We repair commercial ACs across Ranchi and nearby towns" is more useful than "Your trusted cooling partner."&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Who the website is for
&lt;/h2&gt;

&lt;p&gt;Name the main audience. Staff, walk-in customers, people comparing options on Google, partners, donors? If you have more than one audience, rank them. The homepage should speak to the primary one first.&lt;/p&gt;

&lt;p&gt;Also note how people usually find you today: WhatsApp, Google Maps, referrals, Instagram. That tells us whether the site mainly needs to convert search traffic, support an existing conversation, or both.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The jobs the website must do on day one
&lt;/h2&gt;

&lt;p&gt;List five to eight concrete jobs. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Show services and prices clearly enough that people stop asking the same questions on WhatsApp&lt;/li&gt;
&lt;li&gt;Collect enquiries with name, phone and a short message&lt;/li&gt;
&lt;li&gt;Display real photos of work or the premises&lt;/li&gt;
&lt;li&gt;List opening hours and a map&lt;/li&gt;
&lt;li&gt;Let customers book a slot or place an order&lt;/li&gt;
&lt;li&gt;Publish blog posts or updates you will write yourself&lt;/li&gt;
&lt;li&gt;Sell products online with payment and delivery options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Separate "must have for launch" from "nice later." A smaller first version ships sooner and costs less. You can always add a shop or a booking system in phase two.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Pages you already know you need
&lt;/h2&gt;

&lt;p&gt;A typical small-business site needs a home page, an about page, a services or products section, a contact page, and often a FAQ. Add case studies, a blog, careers or a client login only if you will keep them updated.&lt;/p&gt;

&lt;p&gt;If you already have rough copy or a competitor site you like the structure of, link it. Say what you like about it ("clear service cards", "simple contact form") rather than "make it look like this."&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Brand and content you already have
&lt;/h2&gt;

&lt;p&gt;List what exists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logo files (preferably SVG or high-resolution PNG)&lt;/li&gt;
&lt;li&gt;Brand colours and fonts, if any&lt;/li&gt;
&lt;li&gt;Photos you own the rights to&lt;/li&gt;
&lt;li&gt;Existing text from brochures or social posts&lt;/li&gt;
&lt;li&gt;Domain name and where it is registered&lt;/li&gt;
&lt;li&gt;Any current website URL, even if it is outdated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have no logo or photos yet, say so. It is better to plan for that in the timeline than to discover it in week three.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Technical and business constraints
&lt;/h2&gt;

&lt;p&gt;Write down anything that will affect the build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Must work well on mobile (assume yes for almost every Indian business)&lt;/li&gt;
&lt;li&gt;Languages: English only, or Hindi as well?&lt;/li&gt;
&lt;li&gt;Who will update content after launch — you, your staff, or the agency?&lt;/li&gt;
&lt;li&gt;Any tools it must connect to (WhatsApp, payment gateway, Google Analytics, CRM, Tally)&lt;/li&gt;
&lt;li&gt;Hosting preference, if you have one&lt;/li&gt;
&lt;li&gt;Rough budget range and ideal go-live date&lt;/li&gt;
&lt;li&gt;Compliance needs (for example, a privacy policy, or healthcare/education rules)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You do not need to choose WordPress vs custom code yourself. A good team will recommend that after reading the brief. What they need from you is the constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Success looks like…
&lt;/h2&gt;

&lt;p&gt;Finish with how you will judge the site three months after launch. Examples: more qualified WhatsApp enquiries, fewer repeated phone questions, the ability to send a single link instead of a PDF, or a working online store. Clear goals keep the design and the copy honest.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use the brief
&lt;/h2&gt;

&lt;p&gt;Send the same brief to every vendor you speak to. Compare quotes against it. When the first design or staging link arrives, check it against the "jobs on day one" list before you debate colours.&lt;/p&gt;

&lt;p&gt;If you want a sense of how a structured website project usually runs from discovery through launch, we outline our approach on our &lt;a href="https://creworktech.in/website-development-in-ranchi" rel="noopener noreferrer"&gt;website development in Ranchi&lt;/a&gt; page. Use that as a reference for the kinds of questions a team should ask you, not as a substitute for your own brief.&lt;/p&gt;

&lt;p&gt;A clear brief will not make a weak vendor excellent. It will make a good vendor faster, and it will make a bad quote easier to spot. That alone is worth the hour it takes to write.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>business</category>
      <category>india</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What Building a Multi-Outlet Pizza Ordering App Taught Me About Location, Time Zones and Never Blocking the User</title>
      <dc:creator>Akhil Baraik</dc:creator>
      <pubDate>Mon, 12 Oct 2026 04:38:07 +0000</pubDate>
      <link>https://dev.to/akhilbaraik/what-building-a-multi-outlet-pizza-ordering-app-taught-me-about-location-time-zones-and-never-1hdd</link>
      <guid>https://dev.to/akhilbaraik/what-building-a-multi-outlet-pizza-ordering-app-taught-me-about-location-time-zones-and-never-1hdd</guid>
      <description>&lt;p&gt;Food ordering apps look simple from the outside. You open the app, pick a pizza, pay, and wait for the doorbell. When my team at Crework Tech started work on the SUSI Pizza platform, I thought the hard parts would be payments and push notifications. They turned out to be the easy parts. The real work was in deciding which store serves a customer, what time it is for that store, and how to say "no" to a customer without making them close the app.&lt;/p&gt;

&lt;p&gt;This post is a walk through the engineering decisions that mattered most. If you are building anything with multiple physical locations, delivery zones or opening hours, I hope some of it saves you a few late nights.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;SUSI Pizza is a multi-outlet pizza and cake chain. Each outlet has its own delivery radius, its own opening hours and its own menu. Before the app, ordering was mostly manual and phone based. Our scope covered three pieces:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A customer mobile app built in React Native, with Redux for state.&lt;/li&gt;
&lt;li&gt;A React admin panel for the product catalogue, per-store menus and pricing, banners, orders, store settings and staff roles.&lt;/li&gt;
&lt;li&gt;A Node.js and Express REST API on Firebase Firestore, with Firebase Auth (Google Sign-In plus mobile OTP), role-based access for super admins and store admins, and push notifications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A separate team built the store app and the delivery-partner app. Both talk to the same backend, so our API had to be the single source of truth for every outlet. Payments run through Razorpay for UPI, cards and wallets, with Cash on Delivery as a fallback. Indian customers still like having that choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 1: Which store serves this customer?
&lt;/h2&gt;

&lt;p&gt;Given a customer's GPS position, the backend has to find the nearest store that is both in range and open right now. That sounds like one query. In practice it is three different answers that the app must treat differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A store covers you and it is open.&lt;/strong&gt; Show the menu, allow checkout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A store covers you but it is closed right now.&lt;/strong&gt; Tell the customer exactly that, and when possible, when it opens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No store covers your location at all.&lt;/strong&gt; Say so honestly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The early version collapsed the last two into one generic error. From the customer's side, "we don't deliver here" and "come back at 11" are very different messages. Splitting them made the app feel much more honest, and it also gave the client clearer data about where demand was coming from.&lt;/p&gt;

&lt;p&gt;We also found a subtle bug in the fallback path. When the customer's location could not be resolved cleanly, one branch of code quietly used a store's own coordinates in place of the customer's. Distance from a store to itself is zero, so an out-of-range store could be labelled as deliverable. Nothing crashed. The numbers just looked plausible. The fix was simple once we saw it: never substitute a store location for a customer location, and treat "location unknown" as its own state rather than borrowing a value from somewhere else.&lt;/p&gt;

&lt;p&gt;My takeaway: when a function can return "no", make it say &lt;em&gt;why&lt;/em&gt;. An enum of reasons is far easier to debug and to design UI around than a boolean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 2: What time is it, really?
&lt;/h2&gt;

&lt;p&gt;Every store has opening hours in Indian Standard Time. Servers and cloud functions often do not run in IST. If you write &lt;code&gt;new Date().getHours()&lt;/code&gt; on a server set to UTC, your "open at 11 AM" check is off by five and a half hours, and the half hour makes it even easier to miss in testing.&lt;/p&gt;

&lt;p&gt;This bug class came back more than once in different corners of the code, so we stopped fixing it case by case. Store hours logic now always converts to the IST time zone explicitly before comparing, rather than trusting the machine's local time. If you are in India and deploy to any global cloud region, I would make this a rule from day one. Also test the edges: just before opening, just after closing, and stores that close after midnight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 3: Products are not one size fits all
&lt;/h2&gt;

&lt;p&gt;Pizzas have sizes. Cakes have weights. Drinks have serving counts. Some items have spice levels. Hardcoding a "size" field would have broken the moment the client added a new category.&lt;/p&gt;

&lt;p&gt;We built an admin-configurable variant type system. An admin defines a variant type such as Size, Weight, Serving, Volume or Spice Level, and each product can have options under it. Every option carries its own price, discount price, stock and SKU. The customer app reads this generically, so product cards and the cart render correctly without new app releases for every new kind of product.&lt;/p&gt;

&lt;p&gt;The lesson here is the usual one, but it is worth repeating: if the client will keep adding things, make the &lt;em&gt;shape&lt;/em&gt; configurable, not just the values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 4: Copying menus across stores
&lt;/h2&gt;

&lt;p&gt;With multiple outlets each carrying dozens of products, assigning and pricing items store by store was slow, repetitive work for the admin team. We added a "copy from another store" option right inside the assign flow, and a bulk tool that clones an entire menu with full pricing to another store. In one case that meant cloning 90 products in a single action instead of entering them one by one.&lt;/p&gt;

&lt;p&gt;Admin tooling rarely makes it into launch announcements, but it decides whether the client actually keeps their data up to date. If updating the menu is painful, the menu goes stale, and then the customer app is wrong no matter how good the code is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 5: Never block browsing
&lt;/h2&gt;

&lt;p&gt;This became a product principle across the whole app. A closed store, an out-of-range address or an unresolved location should never fully lock the customer out. They can always browse the menu and add items to the cart. Only checkout is gated on real delivery eligibility.&lt;/p&gt;

&lt;p&gt;It sounds obvious, but it touched a lot of code: the store selection flow, cart validation, and the order calculation on the server. The server always re-validates price and eligibility at checkout, so the client app can be relaxed about browsing without ever trusting itself on money.&lt;/p&gt;

&lt;h2&gt;
  
  
  Promotions as rules, not campaigns
&lt;/h2&gt;

&lt;p&gt;The client wanted offers like Buy X Get Y, combos, minimum order value discounts and free delivery above a threshold. Instead of writing code for each campaign, we built a rule-based promotion engine that evaluates whatever rules the admin configures. It also shows "locked" offers, so a customer can see what they need to add to unlock a deal. That one small UI choice turns a promotion from a hidden discount into a nudge.&lt;/p&gt;

&lt;h2&gt;
  
  
  The small things that add up
&lt;/h2&gt;

&lt;p&gt;A few other items that took real time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rate limiting on auth endpoints, because OTP endpoints attract abuse.&lt;/li&gt;
&lt;li&gt;React Native touch responder and scroll performance fixes, so long menus stay smooth on budget Android phones.&lt;/li&gt;
&lt;li&gt;Live order tracking through Received, Preparing, Out for Delivery and Delivered, which stays visible as the customer moves around the app.&lt;/li&gt;
&lt;li&gt;One-tap reorder from order history, since most food orders are repeats.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And one habit I now push on every project: for every location or pricing bug fix, we verified the result against live production data before closing it, instead of shipping on faith because the unit test passed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell another team
&lt;/h2&gt;

&lt;p&gt;If you are building a multi-location ordering product, here is my short list:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Model "why not" explicitly. Out of range and closed are different states.&lt;/li&gt;
&lt;li&gt;Never trust server local time. Convert to the store's time zone every time.&lt;/li&gt;
&lt;li&gt;Make product variants configurable from the admin side.&lt;/li&gt;
&lt;li&gt;Invest in bulk admin tools early.&lt;/li&gt;
&lt;li&gt;Let users browse freely, and gate only the action that truly needs it.&lt;/li&gt;
&lt;li&gt;Validate prices on the server, always.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can read the full &lt;a href="https://creworktech.in/case-studies/susi-pizza-app" rel="noopener noreferrer"&gt;SUSI Pizza case study&lt;/a&gt; on our site, including the platform breakdown and the customer journey. If you are a business in Jharkhand thinking about an app of your own, we also have a page on our &lt;a href="https://creworktech.in/mobile-app-development-in-ranchi" rel="noopener noreferrer"&gt;mobile app development work in Ranchi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I would love to hear how other teams handle store hours and delivery zones. Drop your approach in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Akhil Baraik is the founder of Crework Tech, an IT and digital marketing agency based in Ranchi, Jharkhand. &lt;a href="https://www.linkedin.com/in/akhil-baraik-09613b109/" rel="noopener noreferrer"&gt;Connect with him on LinkedIn&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>node</category>
      <category>firebase</category>
      <category>mobile</category>
    </item>
    <item>
      <title>How Small Businesses in India Can Choose the Right Software Development Company</title>
      <dc:creator>Akhil Baraik</dc:creator>
      <pubDate>Mon, 12 Oct 2026 04:07:35 +0000</pubDate>
      <link>https://dev.to/akhilbaraik/how-small-businesses-in-india-can-choose-the-right-software-development-company-1272</link>
      <guid>https://dev.to/akhilbaraik/how-small-businesses-in-india-can-choose-the-right-software-development-company-1272</guid>
      <description>&lt;p&gt;I run a small IT and digital marketing agency in Ranchi, and I have sat on both sides of the table: pitching for software projects and hiring outside developers. In my experience, most failed projects do not fail because the code was bad. They fail because the business and the developer never agreed on what was being built, who owned it, or what would happen after launch.&lt;/p&gt;

&lt;p&gt;If you run a small business in India and plan to get custom software built, this is what I wish every client read first. It applies whether you hire locally, in a metro, or abroad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start by Defining Your Requirements
&lt;/h2&gt;

&lt;p&gt;Before you speak to any vendor, write down what you need. A few pages in plain language are enough. Answer these questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What problem are you solving, and for whom?&lt;/li&gt;
&lt;li&gt;Who will use the software? Staff, customers, field technicians, managers?&lt;/li&gt;
&lt;li&gt;What are the five or six things the software must do on day one?&lt;/li&gt;
&lt;li&gt;What can wait for a second phase?&lt;/li&gt;
&lt;li&gt;Does it need to connect with anything you already use, such as Tally, a payment gateway, WhatsApp or an existing website?&lt;/li&gt;
&lt;li&gt;What is your rough budget and your ideal launch date?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Separating "must have" from "nice to have" keeps the first version smaller and cheaper, and gives you a fair way to compare quotes. If two quotes differ widely, check whether both companies understood the same scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixed Price vs Time and Material
&lt;/h2&gt;

&lt;p&gt;You will usually be offered one of two pricing models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixed price&lt;/strong&gt; means you agree on a defined scope and a set cost. It works well when your requirements are clear and unlikely to change. The risk is that every change after signing becomes a "change request" with extra cost, and some vendors pad the quote to protect themselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time and material&lt;/strong&gt; means you pay for the hours or days actually worked, usually at an agreed rate. It suits projects where you expect to learn and adjust as you go. The risk is that costs can grow if nobody is watching the scope.&lt;/p&gt;

&lt;p&gt;For many small businesses, a mix works best. Fix the price for a clearly defined first version, then move to time and material for improvements later. Whichever model you choose, ask for payment to be linked to milestones you can actually see and test, not just to dates on a calendar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check Portfolios and Case Studies Properly
&lt;/h2&gt;

&lt;p&gt;Almost every company has a portfolio page. The trick is to look past the screenshots. When you review past work, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Was this project similar to mine in size and type?&lt;/li&gt;
&lt;li&gt;What exactly did this company build? The whole system, or just the design?&lt;/li&gt;
&lt;li&gt;Can I see a live version or a demo?&lt;/li&gt;
&lt;li&gt;Can I speak to a past client?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good case study explains the problem, the approach and the technology used. As one example from my own work, we wrote up a &lt;a href="https://creworktech.in/case-studies/seekoji-electric-erp" rel="noopener noreferrer"&gt;custom after-sales service management system we built for Seekoji Electric&lt;/a&gt;, which used Next.js, Node.js, Express and MongoDB, with JWT authentication and role-based access so different staff saw only what they needed. I mention it only to show the kind of detail you should expect. If a company cannot tell you what they used and why, be careful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask Questions About the Tech Stack
&lt;/h2&gt;

&lt;p&gt;You do not need to be a programmer to ask sensible questions. The goal is software that will not be hard to maintain or move later. Ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which languages and frameworks will you use, and why are they right for this project?&lt;/li&gt;
&lt;li&gt;Are these technologies widely used, so that another developer could take over if needed?&lt;/li&gt;
&lt;li&gt;Where will the software be hosted, and who pays for the hosting?&lt;/li&gt;
&lt;li&gt;How will you handle security, logins and user permissions?&lt;/li&gt;
&lt;li&gt;Will the software work well on mobile phones?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be wary of anyone who insists on an obscure or in-house framework without a clear reason. Popular, well-documented tools make it much easier to find help in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make Sure You Own the Code and IP
&lt;/h2&gt;

&lt;p&gt;This is the point small businesses most often overlook. If you are paying for custom software, your contract should clearly state that the source code, designs, database structure and related intellectual property belong to you once you have paid.&lt;/p&gt;

&lt;p&gt;Also ask for practical access, not just legal ownership:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The code should live in a repository, such as GitHub or GitLab, that you control or can be added to.&lt;/li&gt;
&lt;li&gt;Hosting, domain and cloud accounts should ideally be registered in your business name.&lt;/li&gt;
&lt;li&gt;You should receive all passwords, admin credentials and documentation at handover.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have seen businesses locked out of their own software because a freelancer registered everything personally and then became unreachable.&lt;/p&gt;

&lt;h2&gt;
  
  
  NDAs and Contracts
&lt;/h2&gt;

&lt;p&gt;If you are sharing business ideas, customer data or internal processes, it is reasonable to ask for a non-disclosure agreement before detailed discussions. Most professional companies will sign one without fuss.&lt;/p&gt;

&lt;p&gt;The main contract should cover at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The scope of work, ideally with your requirements document attached&lt;/li&gt;
&lt;li&gt;Milestones, deliverables and payment terms&lt;/li&gt;
&lt;li&gt;Ownership of code and IP&lt;/li&gt;
&lt;li&gt;How change requests are priced and approved&lt;/li&gt;
&lt;li&gt;Warranty or bug-fixing period after launch&lt;/li&gt;
&lt;li&gt;Confidentiality and handling of your data&lt;/li&gt;
&lt;li&gt;What happens if either side wants to end the project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are unsure about any clause, a lawyer's review costs far less than a dispute.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communication and Timezone
&lt;/h2&gt;

&lt;p&gt;Good communication matters as much as good coding. Before signing, find out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who will be your single point of contact?&lt;/li&gt;
&lt;li&gt;How often will you get updates, and in what form?&lt;/li&gt;
&lt;li&gt;Which tools will be used, such as WhatsApp, email, Google Meet or a project board?&lt;/li&gt;
&lt;li&gt;Can you see progress regularly, for example a working demo every week or two?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Timezone matters too. A team in India shares your working hours, which makes quick calls easy. If you hire offshore, agree on overlapping meeting hours. You should also be able to discuss your needs in a language you are comfortable with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plan for Post-Launch Support
&lt;/h2&gt;

&lt;p&gt;Software is never really finished at launch. Users will find bugs, phones and browsers will update, and you will want small changes. Ask each company:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How long is the free bug-fixing period after launch?&lt;/li&gt;
&lt;li&gt;What does a support or maintenance plan include, and what does it cost?&lt;/li&gt;
&lt;li&gt;How fast will they respond to a critical problem?&lt;/li&gt;
&lt;li&gt;Who handles server updates, backups and security patches?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get these answers in writing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Red Flags to Watch For
&lt;/h2&gt;

&lt;p&gt;Take these warning signs seriously:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A quote given before they have understood your requirements&lt;/li&gt;
&lt;li&gt;A price far lower than every other quote, with no clear explanation&lt;/li&gt;
&lt;li&gt;Reluctance to put ownership of code in writing&lt;/li&gt;
&lt;li&gt;No real portfolio, or work they cannot explain in detail&lt;/li&gt;
&lt;li&gt;Pressure to pay most of the amount upfront&lt;/li&gt;
&lt;li&gt;Vague answers about who will actually work on your project&lt;/li&gt;
&lt;li&gt;No mention of testing, documentation or support&lt;/li&gt;
&lt;li&gt;Promises that sound too good, such as a complex app in a week&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One red flag is not always a deal breaker, but two or three together usually are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local vs Metro vs Offshore Agencies
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Local agencies&lt;/strong&gt; in your own city or state can meet you in person, understand regional customers and often work at reasonable rates. Smaller cities now have capable teams, but check their experience carefully, because quality varies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metro agencies&lt;/strong&gt; in places like Bengaluru, Mumbai, Delhi NCR or Pune may have larger teams and more specialists. They can also be more expensive, and a small project may not get their senior people's attention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offshore or foreign agencies&lt;/strong&gt; can bring different experience, but timezone gaps, higher costs and less familiarity with Indian business practices, such as GST invoicing or local payment methods, can make things harder for a small Indian business.&lt;/p&gt;

&lt;p&gt;Judge the team, not the location. A focused small team that understands your business can serve you better than a big name that treats your project as a minor account.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Short Checklist
&lt;/h2&gt;

&lt;p&gt;Before you sign, make sure you can tick these boxes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I have a written list of requirements, split into must have and later.&lt;/li&gt;
&lt;li&gt;I have compared at least two or three quotes for the same scope.&lt;/li&gt;
&lt;li&gt;I understand whether the price is fixed or time and material.&lt;/li&gt;
&lt;li&gt;I have seen real, relevant past work and ideally spoken to a past client.&lt;/li&gt;
&lt;li&gt;I know which technologies will be used and why.&lt;/li&gt;
&lt;li&gt;The contract says I own the code and IP after payment.&lt;/li&gt;
&lt;li&gt;Code, hosting and domain accounts will be in my control.&lt;/li&gt;
&lt;li&gt;An NDA is signed if I am sharing sensitive information.&lt;/li&gt;
&lt;li&gt;Payments are tied to milestones I can test.&lt;/li&gt;
&lt;li&gt;I know who my contact is and how often I will get updates.&lt;/li&gt;
&lt;li&gt;A bug-fixing period and support plan are agreed in writing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Choosing a software development company is less about finding a perfect vendor and more about asking the right questions early.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Akhil Baraik is the founder of Crework Tech, an IT and digital marketing agency based in Ranchi, Jharkhand.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>startup</category>
      <category>business</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Building an After-Sales Service System for an Electrical Manufacturer with Next.js, Express and MongoDB</title>
      <dc:creator>Akhil Baraik</dc:creator>
      <pubDate>Fri, 09 Oct 2026 06:12:06 +0000</pubDate>
      <link>https://dev.to/akhilbaraik/building-an-after-sales-service-system-for-an-electrical-manufacturer-with-nextjs-express-and-2dkl</link>
      <guid>https://dev.to/akhilbaraik/building-an-after-sales-service-system-for-an-electrical-manufacturer-with-nextjs-express-and-2dkl</guid>
      <description>&lt;p&gt;A lot of our work at Crework Tech is marketing-facing: websites, SEO and campaigns. So when Seekoji Electric, an electrical manufacturing and service company here in Ranchi, asked us for a system to run their after-sales service, it was a different kind of project. Nobody would see it on Google. Its whole job was to keep products, customers, technicians and paperwork from drifting out of sync.&lt;/p&gt;

&lt;p&gt;This post walks through how we thought about it and a few patterns that I think are useful for anyone building a similar ERP-style app. The code below is simplified and generic. It shows the shape of the approach, not a copy of the production codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the system needed to do
&lt;/h2&gt;

&lt;p&gt;The scope came down to ten modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard and analytics&lt;/li&gt;
&lt;li&gt;Customer management&lt;/li&gt;
&lt;li&gt;Service request management&lt;/li&gt;
&lt;li&gt;Repair workflow&lt;/li&gt;
&lt;li&gt;Product testing workflow (assign products for testing and update results)&lt;/li&gt;
&lt;li&gt;Technician assignment&lt;/li&gt;
&lt;li&gt;User and role management (RBAC)&lt;/li&gt;
&lt;li&gt;Reports and analytics&lt;/li&gt;
&lt;li&gt;Status tracking&lt;/li&gt;
&lt;li&gt;Search and filters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every product that comes in for service moves through six statuses: &lt;strong&gt;pending, under testing, received, under repair, repaired&lt;/strong&gt; and &lt;strong&gt;delivered&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The stack was Next.js and Tailwind CSS on the front end, Node.js and Express.js for REST APIs, MongoDB for storage, and JWT authentication with role-based access control.&lt;/p&gt;

&lt;p&gt;If you look at that module list, almost all of it hangs off one thing: the status of a repair. The dashboard counts by status. Reports group by status. Technicians filter by status. Customers ask "what's the status?" So that is where I'll start.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Treat status as a state machine, not a dropdown
&lt;/h2&gt;

&lt;p&gt;The easiest way to build a status field is a &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; that lets anyone pick any value. The easiest way to break a service workflow is the same thing. A product jumps from "pending" to "delivered", and now your history says something that never happened.&lt;/p&gt;

&lt;p&gt;The fix is to define the allowed transitions in one place on the server and refuse everything else.&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;// lib/repairStatus.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;STATUS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;PENDING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;UNDER_TESTING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;under_testing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;RECEIVED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;received&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;UNDER_REPAIR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;under_repair&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;REPAIRED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repaired&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;DELIVERED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Example transition map. The real edges are a business decision,&lt;/span&gt;
&lt;span class="c1"&gt;// so agree on them with the people who run the service desk.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TRANSITIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PENDING&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNDER_TESTING&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNDER_TESTING&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RECEIVED&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RECEIVED&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNDER_REPAIR&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNDER_REPAIR&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REPAIRED&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REPAIRED&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DELIVERED&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DELIVERED&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;canTransition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;TRANSITIONS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&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="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TRANSITIONS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;canTransition&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things I like about keeping this as plain data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The front end can import the same map (or fetch it from an endpoint) and only show the buttons that are valid for the current status. The UI stops offering mistakes.&lt;/li&gt;
&lt;li&gt;When the business changes its process, you change one object and the tests that cover it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Checking &lt;code&gt;canTransition&lt;/code&gt; in a route is not enough on its own, though. If two people update the same record at nearly the same moment, a read-then-write can let both through. Put the expected current status in the update filter so MongoDB does the check atomically:&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;// services/repairService.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Repair&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../models/Repair&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;canTransition&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/repairStatus&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;changeStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repairId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;note&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="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;canTransition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;err&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Cannot move from &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&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;updated&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;Repair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOneAndUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;repairId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;          &lt;span class="c1"&gt;// only if still in "from"&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;$push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;note&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;new&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;updated&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;err&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;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Status changed by someone else. Reload and try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;409&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&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;updated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;changeStatus&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;history&lt;/code&gt; push in the same update is what gives you a complete service history for free. Every status change records who made it and when, and it can never get out of step with the status itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. JWT authentication plus RBAC middleware in Express
&lt;/h2&gt;

&lt;p&gt;A service system has very different users. Someone at the front desk creates service requests. A technician updates test results and repairs. An admin manages users and roles. Role-based access control keeps each of them to what their job needs.&lt;/p&gt;

&lt;p&gt;I prefer two small middlewares: one that verifies the token, and one that checks permissions. Mapping roles to permissions (instead of checking role names inside routes) keeps things flexible when a new role shows up later.&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;// middleware/auth.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jsonwebtoken&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;header&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer &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;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;try&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid or expired token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Example roles and permissions. Adjust to the client's real org chart.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PERMISSIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;users:manage&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;repairs:read&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;repairs:write&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;repairs:assign&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;repairs:status&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;reports:read&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repairs:read&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;repairs:write&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;repairs:assign&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;repairs:status&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;reports:read&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;technician&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repairs:read&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;repairs:test&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;repairs:status&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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;granted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PERMISSIONS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&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;ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;granted&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="nx"&gt;p&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;ok&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&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="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Forbidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;next&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authorize&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Signing the token at login is the usual &lt;code&gt;jwt.sign({ sub: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '8h' })&lt;/code&gt;. Then routes read almost like documentation:&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;// routes/repairs.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nc"&gt;Router&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;authenticate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authorize&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../middleware/auth&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;ctrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../controllers/repairs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repairs:read&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repairs:write&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:id/assign&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repairs:assign&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assignTechnician&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:id/test-result&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repairs:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recordTestResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/:id/status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;repairs:status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;ctrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;changeStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One note: route-level permissions are the first gate, not the only one. Inside &lt;code&gt;changeStatus&lt;/code&gt; it is worth also checking that, for example, a technician can only move repairs assigned to them. Row-level rules like that live in the service layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A MongoDB schema sketch for repairs
&lt;/h2&gt;

&lt;p&gt;Here is the rough shape of a repair document. Customers and users live in their own collections and are referenced by id. Test results and history are embedded, because they belong to one repair and are always read with it.&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;// models/Repair.js&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;Schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Types&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mongoose&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;STATUS&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../lib/repairStatus&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;TestResultSchema&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;Schema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass&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;fail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;notes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;testedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;testedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HistorySchema&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;Schema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RepairSchema&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;Schema&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;ticketNo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Customer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;serialNo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;complaint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PENDING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;assignedTechnician&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;testResults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;TestResultSchema&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HistorySchema&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timestamps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Supports the common "my open jobs" and dashboard queries&lt;/span&gt;
&lt;span class="nx"&gt;RepairSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;assignedTechnician&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Supports the search box&lt;/span&gt;
&lt;span class="nx"&gt;RepairSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ticketNo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&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;product.serialNo&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;text&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;product.model&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;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Repair&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RepairSchema&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Design your indexes from the screens, not the other way round. Search and filters, the technician's work queue and the dashboard each turn into a query, and each query should hit an index.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Dashboard numbers from one aggregation
&lt;/h2&gt;

&lt;p&gt;Once status is reliable, the dashboard is mostly a &lt;code&gt;$group&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counts&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;Repair&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aggregate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;$sum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="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;The Next.js dashboard calls a REST endpoint that returns this, and Tailwind takes care of turning it into readable cards. Reports are the same idea with a date range and more grouping keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  The outcome, in plain terms
&lt;/h2&gt;

&lt;p&gt;The benefits we set out for this project are all about the day-to-day work: faster repair processing, centralized service records, better technician productivity, a complete service history, real-time status tracking, less manual paperwork, better operational visibility and an improved experience for their customers. We've written up the project in more detail in our &lt;a href="https://creworktech.in/case-studies/seekoji-electric-erp" rel="noopener noreferrer"&gt;Seekoji Electric ERP case study&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd pass on
&lt;/h2&gt;

&lt;p&gt;If you are building something similar for a manufacturer or service business:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model the workflow before the screens.&lt;/strong&gt; Sit with the people who handle the products and write the status transitions down. That object becomes the spine of the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce transitions on the server, atomically.&lt;/strong&gt; The UI can guide people, but the database filter is what keeps data honest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write history in the same update as the status.&lt;/strong&gt; You will need it the first time a customer disputes what happened to their product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use permissions, not role checks, in routes.&lt;/strong&gt; New roles stop being a refactor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index for the screens people actually use.&lt;/strong&gt; Search, filters and work queues should never scan the whole collection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is new or clever. It is just the stuff that is easy to skip when a project starts as "a simple form to track repairs", and painful to add later.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Akhil Baraik is the founder of Crework Tech, a digital marketing and IT agency based in Ranchi, Jharkhand. You can find him on &lt;a href="https://www.linkedin.com/in/akhil-baraik-09613b109/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>mongodb</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
