<?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: Aqib Javaid</title>
    <description>The latest articles on DEV Community by Aqib Javaid (@aqib_javaid).</description>
    <link>https://dev.to/aqib_javaid</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%2F4145615%2Fbcfee141-e994-4c88-9b3b-0cde889296b2.jpg</url>
      <title>DEV Community: Aqib Javaid</title>
      <link>https://dev.to/aqib_javaid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aqib_javaid"/>
    <language>en</language>
    <item>
      <title>Multi-Tenant SaaS: Shared Database or One Per Tenant?</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Sat, 17 Oct 2026 05:32:20 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/multi-tenant-saas-shared-database-or-one-per-tenant-1kf3</link>
      <guid>https://dev.to/aqib_javaid/multi-tenant-saas-shared-database-or-one-per-tenant-1kf3</guid>
      <description>&lt;p&gt;If you are building a SaaS product for more than one customer, you will have to answer one early question: &lt;strong&gt;how do you separate each customer's data?&lt;/strong&gt; The choice between a shared database and a database per tenant shapes security, cost, performance and how painful your first enterprise customer will be.&lt;/p&gt;

&lt;p&gt;I make this decision with founders as part of &lt;a href="https://aqibjavaid.site/services/saas-development" rel="noopener noreferrer"&gt;SaaS development&lt;/a&gt; work. This guide explains the three common models, when each fits, and the mistakes that are expensive to fix later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "multi-tenant" means
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;tenant&lt;/strong&gt; is one customer account, usually a company with its own users and data. A multi-tenant system serves many tenants from one codebase and one deployment. The alternative, running a separate copy of the whole app per customer, is single-tenant, and it gets hard to maintain quickly.&lt;/p&gt;

&lt;p&gt;Multi-tenant design answers two questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation:&lt;/strong&gt; how do you make sure tenant A can never see tenant B's data?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operations:&lt;/strong&gt; how do you migrate, back up, scale and support many tenants without the work growing with every new customer?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The three data models
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Shared database, shared schema
&lt;/h3&gt;

&lt;p&gt;All tenants live in the same tables. Each row has a &lt;code&gt;tenant_id&lt;/code&gt; column, and every query filters by it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cheapest to run and simplest to start&lt;/li&gt;
&lt;li&gt;One migration updates everyone&lt;/li&gt;
&lt;li&gt;Easy to report across tenants&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One missed filter leaks data between customers&lt;/li&gt;
&lt;li&gt;A very large tenant can slow everyone down ("noisy neighbour")&lt;/li&gt;
&lt;li&gt;Restoring a single tenant from a backup is awkward&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Shared database, separate schema per tenant
&lt;/h3&gt;

&lt;p&gt;Tenants share a database server, but each gets its own schema, such as separate sets of tables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stronger separation than a tenant column&lt;/li&gt;
&lt;li&gt;Per-tenant backup and restore is easier&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Migrations must run once per tenant, which gets slow with many tenants&lt;/li&gt;
&lt;li&gt;Tooling and connection handling are more complex&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Separate database per tenant
&lt;/h3&gt;

&lt;p&gt;Each tenant has its own database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strongest isolation, which helps with security reviews and regulated customers&lt;/li&gt;
&lt;li&gt;A tenant's data can be moved, backed up or deleted on its own&lt;/li&gt;
&lt;li&gt;Heavy tenants don't affect the others' queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Highest operational cost and complexity&lt;/li&gt;
&lt;li&gt;Migrations and monitoring multiply with each tenant&lt;/li&gt;
&lt;li&gt;Cross-tenant reporting needs extra work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Microsoft's architecture guidance on &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/guide/multitenant/considerations/tenancy-models" rel="noopener noreferrer"&gt;tenancy models&lt;/a&gt; describes the same trade-off between isolation, cost and operational effort, and is worth reading alongside this.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to choose: five questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Who are your customers?&lt;/strong&gt; Many small customers point toward a shared database. A few large or regulated customers who ask about data isolation point toward separate databases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. What are the compliance expectations?&lt;/strong&gt; If customers need contractual guarantees about data separation, or data residency in specific regions, a separate database makes those promises easier to keep.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. How uneven will tenants be?&lt;/strong&gt; If one tenant could be a hundred times bigger than the average, plan for isolating heavy tenants early.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. How big is the team?&lt;/strong&gt; Every extra database is something to monitor, migrate and back up. A small team should choose the simplest model that meets the requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. How will you grow?&lt;/strong&gt; Many products start shared and move their largest customers to dedicated databases later. A design that allows this is more valuable than a perfect choice on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  My default for early-stage SaaS
&lt;/h2&gt;

&lt;p&gt;For most early products I start with a &lt;strong&gt;shared database and a &lt;code&gt;tenant_id&lt;/code&gt; on every tenant-owned table&lt;/strong&gt;, and I build the safeguards in from day one. This keeps cost and complexity low while the product finds its market, and it does not close the door on moving large customers out later.&lt;/p&gt;

&lt;p&gt;The key is that "shared" must not mean "careless". The safeguards I add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A global scope or middleware&lt;/strong&gt; that applies the tenant filter automatically, so developers can't forget it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database-level protection&lt;/strong&gt; where the database supports it. PostgreSQL's &lt;a href="https://www.postgresql.org/docs/current/ddl-rowsecurity.html" rel="noopener noreferrer"&gt;row-level security&lt;/a&gt; can enforce tenant filtering even if application code has a bug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tenant-aware tests&lt;/strong&gt; that fail if a query returns another tenant's rows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composite unique keys&lt;/strong&gt; that include the tenant, so two customers can use the same email or slug&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tenant context in logs and jobs&lt;/strong&gt;, so background work never runs without knowing whose data it touches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you use Laravel, packages such as &lt;a href="https://tenancyforlaravel.com/" rel="noopener noreferrer"&gt;Tenancy for Laravel&lt;/a&gt; support both single-database and multi-database approaches, which helps you start simple without being locked in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Forgetting background jobs.&lt;/strong&gt; Queued jobs, scheduled tasks and webhooks run outside a web request, so the tenant context can be missing. Pass the tenant explicitly, as I describe in my guide to &lt;a href="https://aqibjavaid.site/blog/laravel-queue-architecture-saas" rel="noopener noreferrer"&gt;Laravel queue architecture&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leaking through shared resources.&lt;/strong&gt; Caches, search indexes and file storage need tenant-aware keys and paths, not only database rows. See &lt;a href="https://aqibjavaid.site/blog/laravel-secure-file-storage-saas" rel="noopener noreferrer"&gt;secure multi-tenant file storage in Laravel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Global unique constraints.&lt;/strong&gt; Uniqueness rules that ignore the tenant make customers collide with each other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No plan for super-admin access.&lt;/strong&gt; Support staff need to view tenant data, and that access should be controlled and logged. My article on &lt;a href="https://aqibjavaid.site/blog/laravel-audit-logging-compliance-saas" rel="noopener noreferrer"&gt;audit logging for SaaS compliance&lt;/a&gt; covers this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choosing separate databases too early.&lt;/strong&gt; It feels safer but multiplies operations for a product that may only have a handful of customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-tenant reporting as an afterthought.&lt;/strong&gt; Analytics across tenants are easy in a shared schema and hard with many databases.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real-world example
&lt;/h2&gt;

&lt;p&gt;On Imperial Votes, a multi-tenant competition platform I built on Next.js and Prisma, organizers run their own competitions with branding tools, role-based permissions and dashboards, while super-admin controls sit above them. That mix of customers acting independently under one platform is exactly where the tenancy decision matters. The &lt;a href="https://aqibjavaid.site/work/imperial-votes-nextjs-saas-voting-platform-case-study" rel="noopener noreferrer"&gt;Imperial Votes case study&lt;/a&gt; shows how the product grew, and the matching write-up on &lt;a href="https://aqibjavaid.site/blog/multi-tenant-voting-saas-nextjs-prisma-case-study" rel="noopener noreferrer"&gt;building a multi-tenant voting SaaS&lt;/a&gt; goes deeper.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to decide before you write code
&lt;/h2&gt;

&lt;p&gt;Before the first migration, write down answers to these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is a tenant in our product (a company, a team, a workspace)?&lt;/li&gt;
&lt;li&gt;Which tables belong to a tenant, and which are global?&lt;/li&gt;
&lt;li&gt;How is the tenant identified on each request: subdomain, path or login?&lt;/li&gt;
&lt;li&gt;How will background jobs and webhooks know the tenant?&lt;/li&gt;
&lt;li&gt;What would force us to move a customer to a dedicated database?&lt;/li&gt;
&lt;li&gt;How do support staff access tenant data safely?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Answering these costs an afternoon. Changing the model after launch costs months.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving a large tenant to its own database later
&lt;/h2&gt;

&lt;p&gt;If you design for it, moving one customer out of the shared database is a manageable project rather than a rewrite. The usual steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the new database with the same schema and run the same migrations.&lt;/li&gt;
&lt;li&gt;Copy the tenant's rows across, using the &lt;code&gt;tenant_id&lt;/code&gt; to select exactly what belongs to them.&lt;/li&gt;
&lt;li&gt;Verify counts and key records, and keep the old data until the checks pass.&lt;/li&gt;
&lt;li&gt;Switch the tenant's connection so requests resolve to the new database.&lt;/li&gt;
&lt;li&gt;Remove the tenant's rows from the shared database once you are confident.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This only works if every tenant-owned table carries the tenant identifier, files and caches are tenant-scoped, and your application resolves the database connection from the tenant. Those are the same safeguards that keep the shared model safe, which is why I add them from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Multi-tenancy is mostly about isolation and operational cost.&lt;/li&gt;
&lt;li&gt;Shared database with a tenant column is the cheapest start. Separate databases give the strongest isolation.&lt;/li&gt;
&lt;li&gt;Add safeguards such as global scopes, row-level security and tenant-aware tests so shared never means careless.&lt;/li&gt;
&lt;li&gt;Plan for jobs, caches, files and support access, not only database tables.&lt;/li&gt;
&lt;li&gt;Design so you can move large tenants out later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are planning a SaaS product and want the architecture settled before you build, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a call&lt;/a&gt; and we can map the tenancy model to your customers and budget.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/multi-tenant-saas-shared-vs-separate-database" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>architecture</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Software Maintenance Cost: What to Budget Each Year</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Fri, 16 Oct 2026 13:39:58 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/software-maintenance-cost-what-to-budget-each-year-mne</link>
      <guid>https://dev.to/aqib_javaid/software-maintenance-cost-what-to-budget-each-year-mne</guid>
      <description>&lt;p&gt;Most people budget carefully for the build and then forget the bill that never stops: &lt;strong&gt;software maintenance cost&lt;/strong&gt;. A web app, SaaS product or internal system is not a one-off purchase. It needs hosting, security patches, dependency upgrades, backups, monitoring and fixes for the problems real users find. If nobody pays for that work, the system quietly decays until it becomes the legacy application you were trying to avoid.&lt;/p&gt;

&lt;p&gt;I look after production systems as part of my &lt;a href="https://aqibjavaid.site/services/cloud-devops-deployment" rel="noopener noreferrer"&gt;cloud, DevOps and deployment work&lt;/a&gt;, so this guide covers what maintenance actually includes, what drives the cost, and how to budget for it without guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What software maintenance actually covers
&lt;/h2&gt;

&lt;p&gt;"Maintenance" is a bucket for four different kinds of work. Knowing which is which makes the budget conversation much easier.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Corrective:&lt;/strong&gt; fixing bugs found in production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preventive:&lt;/strong&gt; security patches, framework and library upgrades, and clean-up that stops problems before they appear.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive:&lt;/strong&gt; changing the system because the world changed, such as a new PHP version, a payment provider update or a browser change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perfective:&lt;/strong&gt; small improvements and requests that make the product better but are not a new project.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;New features are usually a separate budget. The trap is letting small feature requests quietly consume the maintenance hours that keep the system safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  The recurring costs people forget
&lt;/h2&gt;

&lt;p&gt;Beyond developer time, a live system has running costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hosting and infrastructure:&lt;/strong&gt; servers, databases, storage and bandwidth. My comparison of &lt;a href="https://aqibjavaid.site/blog/laravel-hosting-options-cost" rel="noopener noreferrer"&gt;Laravel hosting options and cost&lt;/a&gt; shows how widely this varies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party services:&lt;/strong&gt; email, SMS, payments, search, error tracking and AI APIs, often billed by usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain, SSL and licences&lt;/strong&gt; for paid plugins or tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backups and disaster recovery,&lt;/strong&gt; including actually testing a restore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring and alerting,&lt;/strong&gt; so you hear about problems before customers do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security work:&lt;/strong&gt; patching, access reviews and responding to vulnerability notices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These costs are easy to estimate once you have a list. The surprise usually comes from usage-based services that grow with your traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What drives maintenance cost up or down
&lt;/h2&gt;

&lt;p&gt;I will not quote a percentage that pretends to fit every system. These factors decide where yours lands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Age and technology.&lt;/strong&gt; Systems on unsupported frameworks cost more to keep safe. This is the main link to &lt;a href="https://aqibjavaid.site/blog/rewrite-vs-refactor-legacy-application" rel="noopener noreferrer"&gt;rewriting or refactoring a legacy application&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test coverage.&lt;/strong&gt; Tested code is cheaper to upgrade because you can see what broke.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Number of integrations.&lt;/strong&gt; Every third-party API is something that can change or fail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traffic and data volume.&lt;/strong&gt; More load means more tuning and more infrastructure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality of documentation.&lt;/strong&gt; Missing knowledge makes every task slower.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment process.&lt;/strong&gt; Manual releases cost time and cause errors. A clean pipeline, as in my guide to &lt;a href="https://aqibjavaid.site/blog/laravel-zero-downtime-deployments" rel="noopener noreferrer"&gt;zero-downtime deployments&lt;/a&gt;, lowers the cost of every future change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Required uptime.&lt;/strong&gt; A system that must always be available needs monitoring and on-call, which costs more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three ways to pay for maintenance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Retainer.&lt;/strong&gt; A fixed monthly amount that covers a set number of hours or a defined scope. It suits systems with steady needs and gives you predictable budgeting and fast response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Time and materials.&lt;/strong&gt; You pay for hours as they are used. It is flexible for quiet systems, but costs are unpredictable and urgent issues compete with other clients' work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Fixed-scope packages.&lt;/strong&gt; Specific tasks, such as a framework upgrade or security audit, for a fixed price. These are good for planned one-off work on top of a baseline.&lt;/p&gt;

&lt;p&gt;Many businesses use a small retainer for monitoring and urgent fixes, plus fixed-scope projects for bigger upgrades.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to estimate your own maintenance budget
&lt;/h2&gt;

&lt;p&gt;Use this simple worksheet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;List every running cost:&lt;/strong&gt; hosting, services, domains, licences. Check last month's invoices for each.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Estimate routine hours:&lt;/strong&gt; security patches, dependency updates, backup checks and monitoring review, per month.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a bug-fix allowance&lt;/strong&gt; based on how often production issues have appeared so far.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add planned upgrade work&lt;/strong&gt; for the year, such as a major framework or PHP version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a contingency&lt;/strong&gt; for the unexpected, and treat it as real money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate new features&lt;/strong&gt; into their own budget so they do not eat maintenance time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the system is brand new and has no history, expect the early months to need more attention while real usage reveals problems. A short audit gives you a better starting point than a guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why support dates decide your upgrade calendar
&lt;/h2&gt;

&lt;p&gt;Two clocks set most of your planned maintenance: the language and the framework.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language versions&lt;/strong&gt; each get a limited support window, after which they stop receiving security fixes. PHP publishes these dates on its &lt;a href="https://www.php.net/supported-versions.php" rel="noopener noreferrer"&gt;supported versions page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework versions&lt;/strong&gt; follow their own policy. Laravel documents which releases still receive bug and security fixes in its &lt;a href="https://laravel.com/docs/releases" rel="noopener noreferrer"&gt;release notes and support policy&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Put both sets of dates in a calendar and plan an upgrade before a version runs out of support, not after. Doing one planned upgrade a year is far cheaper and less risky than jumping across several versions at once, which is the situation I describe in my &lt;a href="https://aqibjavaid.site/blog/laravel-upgrade-checklist" rel="noopener noreferrer"&gt;Laravel upgrade checklist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A good maintenance plan therefore includes a yearly "version review": check what you run, check the support dates, and decide what gets upgraded next. It takes a few hours and avoids an emergency later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when you skip maintenance
&lt;/h2&gt;

&lt;p&gt;Skipping maintenance does not save money. It moves the cost to later and makes it larger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Known security holes stay open&lt;/li&gt;
&lt;li&gt;Upgrades pile up until they become a risky big-bang project&lt;/li&gt;
&lt;li&gt;Small bugs turn into outages&lt;/li&gt;
&lt;li&gt;Only one person understands the system&lt;/li&gt;
&lt;li&gt;Hosting and dependencies freeze because nobody dares touch them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the Henceforward WordPress project, the work combined website development with migrating infrastructure to Scaleway with Laravel Forge, strengthening security and optimising the production environment. That kind of foundation is what makes ongoing care cheaper. You can read the &lt;a href="https://aqibjavaid.site/work/henceforward-wordpress-migration-security-optimization" rel="noopener noreferrer"&gt;Henceforward case study&lt;/a&gt; for the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple way to report maintenance value
&lt;/h2&gt;

&lt;p&gt;Maintenance feels invisible when nothing breaks, which makes it easy to cut. Ask for a short monthly note covering four things: what was patched or upgraded, which incidents occurred and how fast they were resolved, how backups and monitoring performed, and what is planned next. A one-page summary turns "nothing happened" into evidence that the system is being looked after, and it gives you the data to adjust the plan when needs change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions to ask before you agree a maintenance plan
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What exactly is included, and what counts as a new feature?&lt;/li&gt;
&lt;li&gt;What is the response time for an urgent production issue?&lt;/li&gt;
&lt;li&gt;Who monitors the system, and how will I be alerted?&lt;/li&gt;
&lt;li&gt;How are backups tested?&lt;/li&gt;
&lt;li&gt;What happens to the plan if the system grows?&lt;/li&gt;
&lt;li&gt;Can I see a report of what was done each month?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A clear answer to these is a better signal than the lowest price. My thoughts on &lt;a href="https://aqibjavaid.site/blog/why-experience-beats-low-cost-outsourcing" rel="noopener noreferrer"&gt;why experience beats low-cost outsourcing&lt;/a&gt; apply here too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Maintenance is four kinds of work: fixing, preventing, adapting and improving.&lt;/li&gt;
&lt;li&gt;Running costs go beyond developer time: hosting, services, backups, monitoring and security.&lt;/li&gt;
&lt;li&gt;Age, tests, integrations and deployment process drive the price more than size alone.&lt;/li&gt;
&lt;li&gt;Choose a retainer for steady needs, fixed-scope packages for planned upgrades.&lt;/li&gt;
&lt;li&gt;Skipping maintenance makes problems larger and more expensive later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a clear maintenance budget for a system you already run, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a call&lt;/a&gt; and I will review it and tell you what it really needs.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/software-maintenance-cost-what-to-budget" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>webdev</category>
      <category>maintenance</category>
      <category>budget</category>
    </item>
    <item>
      <title>Rewrite or Refactor a Legacy Application? How to Decide</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Thu, 15 Oct 2026 13:27:16 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/rewrite-or-refactor-a-legacy-application-how-to-decide-4i13</link>
      <guid>https://dev.to/aqib_javaid/rewrite-or-refactor-a-legacy-application-how-to-decide-4i13</guid>
      <description>&lt;p&gt;Every business with an ageing application eventually faces the same question: do we &lt;strong&gt;rewrite or refactor the legacy application&lt;/strong&gt;? Get it wrong and you either burn a year rebuilding something that already worked, or you pour money into patching a system that cannot be saved.&lt;/p&gt;

&lt;p&gt;I handle this decision as part of &lt;a href="https://aqibjavaid.site/services/legacy-application-modernization" rel="noopener noreferrer"&gt;legacy application modernization&lt;/a&gt; work, and the honest answer is almost never "rewrite everything" or "leave it alone". This guide gives you a practical way to decide, the questions I ask, and the middle path that usually wins.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "legacy" actually means
&lt;/h2&gt;

&lt;p&gt;Old code is not automatically legacy code. An application is legacy when it is &lt;strong&gt;hard to change safely&lt;/strong&gt;. Typical signs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nobody dares to deploy on a Friday, or at all&lt;/li&gt;
&lt;li&gt;Only one person understands how it works&lt;/li&gt;
&lt;li&gt;The framework or language version no longer gets security updates&lt;/li&gt;
&lt;li&gt;Simple changes take weeks because everything is tangled together&lt;/li&gt;
&lt;li&gt;There are no tests, so every change is a gamble&lt;/li&gt;
&lt;li&gt;Hosting and dependencies are frozen because upgrading might break things&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the app is old but stable, tested and easy to change, it is not your problem. If it is new but a mess, it might be.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three options, in plain terms
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Refactor in place.&lt;/strong&gt; Improve the existing code step by step without changing what the system does: add tests, clean up the worst areas, upgrade dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rewrite from scratch.&lt;/strong&gt; Build a new system and switch over when it is ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modernise incrementally.&lt;/strong&gt; Replace the system piece by piece while the old one keeps running. This is often called the strangler pattern, described well in &lt;a href="https://martinfowler.com/bliki/StranglerFigApplication.html" rel="noopener noreferrer"&gt;Martin Fowler's write-up of the Strangler Fig approach&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most teams think they have two choices. The third is what usually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why big-bang rewrites fail so often
&lt;/h2&gt;

&lt;p&gt;A full rewrite looks attractive: clean code, modern tools, no baggage. The problem is that the old system contains years of quiet business knowledge: edge cases, workarounds, rules nobody wrote down. Joel Spolsky made this argument in his classic essay &lt;a href="https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/" rel="noopener noreferrer"&gt;Things You Should Never Do, Part I&lt;/a&gt;, and it still holds up.&lt;/p&gt;

&lt;p&gt;In practice, rewrites go wrong for predictable reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The old system keeps changing&lt;/strong&gt; while you build the new one, so you are chasing a moving target.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You ship nothing for months&lt;/strong&gt;, so the business pays but sees no value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hidden behaviour is lost&lt;/strong&gt; because nobody remembers why it worked that way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope grows&lt;/strong&gt;, because a rewrite invites "while we're at it" features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That does not mean never rewrite. It means be sure you must, and limit the blast radius.&lt;/p&gt;

&lt;h2&gt;
  
  
  A decision framework you can use this week
&lt;/h2&gt;

&lt;p&gt;Score your system on these questions. I use them in audits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Can the system still do its job?&lt;/strong&gt; If it works and the business is not blocked, you have time to move carefully. If it is failing daily, you may need to stabilise first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Is the technology end-of-life?&lt;/strong&gt; An unsupported framework or language version is a security and hiring risk. That pushes you toward upgrading or replacing, not ignoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Is the data model sound?&lt;/strong&gt; A good database under a messy application is a great position. A broken data model is a much bigger problem than ugly code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Can you test it?&lt;/strong&gt; If you can add tests around key flows, refactoring becomes safe. If the code cannot be tested at all, that is a strong signal to replace parts of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. How tangled are the pieces?&lt;/strong&gt; Systems with clear boundaries can be replaced one module at a time. A single ball of mud is harder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. What does the business need next?&lt;/strong&gt; If the next year's roadmap is mostly new features, spend effort where those features land, not on parts nobody touches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Who knows the system?&lt;/strong&gt; If that knowledge is walking out the door, document and test first, whatever you choose.&lt;/p&gt;

&lt;h2&gt;
  
  
  When refactoring is the right call
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;The framework is still supported or has a clear upgrade path&lt;/li&gt;
&lt;li&gt;The data model is sound&lt;/li&gt;
&lt;li&gt;The pain is concentrated in a few areas&lt;/li&gt;
&lt;li&gt;You can put tests around the critical flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I often start with exactly this: map the dependencies, add tests around the riskiest workflows, fix the worst hotspots, and upgrade in steps. My &lt;a href="https://aqibjavaid.site/blog/laravel-upgrade-checklist" rel="noopener noreferrer"&gt;Laravel upgrade checklist&lt;/a&gt; walks through that approach for older Laravel apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a rewrite is justified
&lt;/h2&gt;

&lt;p&gt;A rewrite makes sense when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The platform is end-of-life with no sensible upgrade path&lt;/li&gt;
&lt;li&gt;The architecture fundamentally cannot support what the business needs&lt;/li&gt;
&lt;li&gt;The system is small enough to rebuild in a short, fixed window&lt;/li&gt;
&lt;li&gt;The code is so untestable that incremental change is not possible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even then, I prefer to rebuild in slices, with the old system running beside the new one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The incremental path most businesses should take
&lt;/h2&gt;

&lt;p&gt;The approach I recommend most often has five steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Audit first.&lt;/strong&gt; Understand the workflows, the database, the dependencies and the risks before deciding anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stabilise.&lt;/strong&gt; Fix security gaps, add monitoring and backups, and make deployments repeatable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add tests&lt;/strong&gt; around the flows that matter most.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace one slice at a time.&lt;/strong&gt; Pick a self-contained module, rebuild it in the new stack and route traffic to it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retire the old parts&lt;/strong&gt; once the new ones have proven themselves.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each step delivers something the business can see, and each one can be paused or reversed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What drives the cost either way
&lt;/h2&gt;

&lt;p&gt;I will not quote a number without seeing the system, but these factors move cost and timeline the most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Size and complexity&lt;/strong&gt; of the application, especially the number of distinct workflows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test coverage&lt;/strong&gt;, because untested code costs more to change safely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data migration&lt;/strong&gt;, since moving and validating years of records is often the riskiest step&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations&lt;/strong&gt; with payment, shipping, CRM and other third-party services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How much downtime the business can tolerate&lt;/strong&gt; during the switch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation and access&lt;/strong&gt;, because missing knowledge slows everything down&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An audit before the main work is usually the cheapest way to turn these unknowns into a plan you can budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real-world example
&lt;/h2&gt;

&lt;p&gt;On the &lt;a href="https://aqibjavaid.site/work/jamaica-freight-forwarding-core-php-audit" rel="noopener noreferrer"&gt;PPCWA freight-forwarding audit&lt;/a&gt;, I was brought in to audit and stabilise a nearly completed Core PHP and MySQL platform. The work focused on understanding the existing system first: workflows, database structure, frontend maintainability, dependencies and configuration. I also assessed the architectural path toward a future Laravel migration. The point was to decide with evidence instead of opinion. I describe a related migration in &lt;a href="https://aqibjavaid.site/blog/migrating-legacy-core-php-platform-to-laravel" rel="noopener noreferrer"&gt;migrating a legacy Core PHP platform to Laravel&lt;/a&gt; and a Laravel-specific version in &lt;a href="https://aqibjavaid.site/blog/laravel-5-migration-legacy-application-modernization" rel="noopener noreferrer"&gt;Laravel 5 migration strategy&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions to ask any developer or agency
&lt;/h2&gt;

&lt;p&gt;If someone recommends a rewrite, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What did you find in the existing system that cannot be improved?&lt;/li&gt;
&lt;li&gt;How will we keep the business running during the work?&lt;/li&gt;
&lt;li&gt;Which parts will go live first, and when?&lt;/li&gt;
&lt;li&gt;How will you protect the data and the undocumented business rules?&lt;/li&gt;
&lt;li&gt;What is the plan if the project overruns?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good partner answers with specifics. A weak one answers with enthusiasm for new technology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;"Legacy" means hard to change safely, not just old.&lt;/li&gt;
&lt;li&gt;Big-bang rewrites fail because they lose hidden business knowledge and deliver late.&lt;/li&gt;
&lt;li&gt;Audit first, then choose refactor, rewrite or incremental replacement with evidence.&lt;/li&gt;
&lt;li&gt;Incremental modernisation is usually the lowest-risk option.&lt;/li&gt;
&lt;li&gt;A sound data model and testable flows tilt the decision toward refactoring.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are weighing this decision for your own application, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a call&lt;/a&gt; and I will tell you which path I would take and why.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/rewrite-vs-refactor-legacy-application" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>legacy</category>
      <category>refactoring</category>
      <category>architecture</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>MLS and IDX Integration: A Guide for Real Estate Platforms</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Wed, 14 Oct 2026 05:47:46 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/mls-and-idx-integration-a-guide-for-real-estate-platforms-18bb</link>
      <guid>https://dev.to/aqib_javaid/mls-and-idx-integration-a-guide-for-real-estate-platforms-18bb</guid>
      <description>&lt;p&gt;If you are building a property search site, a broker portal or a real estate app, you will hit the same wall quickly: the listings you want live in a Multiple Listing Service (MLS), and getting them out is a data-integration project with business rules, not a simple API call. This guide explains how &lt;strong&gt;MLS and IDX integration&lt;/strong&gt; works, what usually goes wrong, and how I approach it as part of &lt;a href="https://aqibjavaid.site/services/real-estate-data-platform-development" rel="noopener noreferrer"&gt;real estate data platform development&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I am an engineer, not a lawyer or a licensed broker. MLS rules differ by region and by board, so treat this as an engineering view and confirm the licensing details with the MLS you plan to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  MLS vs IDX: what each one actually is
&lt;/h2&gt;

&lt;p&gt;These two terms get mixed up constantly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MLS (Multiple Listing Service)&lt;/strong&gt; is the database where brokers share listings. Access is controlled by the MLS and its rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IDX (Internet Data Exchange)&lt;/strong&gt; is the policy framework that lets participating brokers display other brokers' listings on their own websites. It governs &lt;em&gt;display&lt;/em&gt;, not unlimited use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data feeds&lt;/strong&gt; are the technical way the data reaches you. Today that is usually an API, and the older approach was a protocol called RETS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last point matters for planning. IDX permission to show listings does not automatically mean you may store them forever, resell them, run analytics on them or train models on them. Each MLS sets its own rules for what a feed can be used for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who can get access to the data
&lt;/h2&gt;

&lt;p&gt;You normally need a relationship with the MLS before any code is written:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;broker or agent&lt;/strong&gt; who is a participant, or a technology vendor sponsored by one&lt;/li&gt;
&lt;li&gt;A signed &lt;strong&gt;data licence or feed agreement&lt;/strong&gt; that lists what you may display and store&lt;/li&gt;
&lt;li&gt;Approval of your site or app before real listings go live&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the first business decision. If you are a software company building for brokers, the broker usually holds the licence and you work on their behalf. Ask early who signs what, because it can take longer than the build.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the data reaches you: RETS vs the RESO Web API
&lt;/h2&gt;

&lt;p&gt;The industry's current standard is the &lt;strong&gt;RESO Web API&lt;/strong&gt;, a RESTful interface built on web standards and OData. RESO's own FAQ describes the older RETS protocol as &lt;a href="https://www.reso.org/knowledge-base/reso-web-api-faq/" rel="noopener noreferrer"&gt;deprecated and unsupported&lt;/a&gt;, and some MLSs are still mid-transition.&lt;/p&gt;

&lt;p&gt;What that means for your project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ask which feed the MLS offers&lt;/strong&gt; before you estimate. A RETS-only MLS and a Web API MLS are very different builds.&lt;/li&gt;
&lt;li&gt;With the Web API you query with filters and page through results, and you can ask for only what changed since your last sync using &lt;a href="https://www.odata.org/" rel="noopener noreferrer"&gt;OData&lt;/a&gt; query options.&lt;/li&gt;
&lt;li&gt;Many MLSs publish field names using the RESO Data Dictionary, which makes the data far easier to normalise across sources than older custom schemas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some teams skip direct MLS integration and use an aggregator or vendor that already normalises several MLSs. That trades flexibility and cost for speed. For a single-market product, going direct is often fine. For many markets, an aggregator can save months.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture I use
&lt;/h2&gt;

&lt;p&gt;Whatever the source, I keep the same shape:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ingest.&lt;/strong&gt; A scheduled job pulls new and changed listings. Always sync incrementally using a modified-timestamp field, with a periodic full reconcile to catch anything missed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store raw.&lt;/strong&gt; Keep the original payload so you can re-process if your mapping changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Normalise.&lt;/strong&gt; Map the source fields to your own schema (price, status, beds, baths, address, media, agent, timestamps). This is where most bugs live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Index for search.&lt;/strong&gt; Put searchable data in a database or search engine tuned for filters like price, location and property type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serve.&lt;/strong&gt; Your site or API reads from your own store, never directly from the MLS on each page view.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor.&lt;/strong&gt; Alert on failed syncs, sudden drops in listing counts and stale data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I covered the general pattern for keeping data pipelines healthy in &lt;a href="https://aqibjavaid.site/blog/scraped-data-change-detection" rel="noopener noreferrer"&gt;scraped data change detection&lt;/a&gt;. The same thinking applies: detect what changed, and know when a feed has quietly stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard parts nobody warns you about
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Status changes and removals.&lt;/strong&gt; A listing that goes pending, sold or withdrawn must disappear or change on your site promptly. Many MLS rules require this, and stale "active" listings annoy users fast. Plan for deletes, not just new rows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Photos and media.&lt;/strong&gt; Images are large and numerous. You often need to cache, resize and serve them yourself, within the MLS's media rules, and handle photos that are added, reordered or removed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Field quirks.&lt;/strong&gt; The same concept appears in different shapes across MLSs: free-text fields, custom picklists, inconsistent address formats. Budget time for a mapping layer you can adjust without a redeploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate limits and quotas.&lt;/strong&gt; Feeds throttle. Page carefully, back off on errors and never fetch everything on every run.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Display rules.&lt;/strong&gt; Required disclaimers, attribution, brokerage names and "last updated" notices are commonly part of the agreement. Build them into the templates from the start.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compliance as a feature.&lt;/strong&gt; Treat the rules as requirements, not paperwork. An audit finding can cost more than the integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost and timeline expectations
&lt;/h2&gt;

&lt;p&gt;I won't quote numbers that depend on your MLS, but these factors move cost and time the most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Number of MLSs.&lt;/strong&gt; Each one has its own agreement, quirks and mapping work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feed type.&lt;/strong&gt; Web API is simpler than RETS, and a vendor aggregator can be simpler still.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope.&lt;/strong&gt; Search only, or also saved searches, alerts, lead capture and an agent portal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Media handling&lt;/strong&gt; and how much you cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance review&lt;/strong&gt; time on the MLS side.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern I see is that the technical build is the smaller part. Approvals and rules take the calendar time, so start the licensing conversation first.&lt;/p&gt;

&lt;h2&gt;
  
  
  A checklist before you commit to a build
&lt;/h2&gt;

&lt;p&gt;Before you sign a statement of work, get answers to these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which MLS or MLSs do we need, and who holds the licence?&lt;/li&gt;
&lt;li&gt;Is the feed a RESO Web API, a legacy RETS service or a vendor aggregator?&lt;/li&gt;
&lt;li&gt;What may we display, store and cache, and for how long?&lt;/li&gt;
&lt;li&gt;How quickly must sold, pending and withdrawn listings be removed?&lt;/li&gt;
&lt;li&gt;Which disclaimers and attribution must appear on every listing page?&lt;/li&gt;
&lt;li&gt;What are the rate limits, and is there a sandbox or test feed?&lt;/li&gt;
&lt;li&gt;Who monitors the sync after launch, and what alerts exist?&lt;/li&gt;
&lt;li&gt;Do we need only search, or also alerts, saved searches and lead capture?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If more than two of these are unanswered, the estimate is a guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real-world parallel from my work
&lt;/h2&gt;

&lt;p&gt;On the Violerts PropTech platform, my work involved multi-agency data ingestion for NYC property compliance, plus asynchronous scraping and real-time alerts. Different data sources, but the same engineering problems: messy upstream formats, changing data, and users who need accurate information quickly. See the &lt;a href="https://aqibjavaid.site/work/violerts-nyc-proptech-compliance-saas" rel="noopener noreferrer"&gt;Violerts case study&lt;/a&gt; for how that platform is structured, and my notes on &lt;a href="https://aqibjavaid.site/blog/third-party-api-integration-best-practices" rel="noopener noreferrer"&gt;third-party API integration best practices&lt;/a&gt; for the general lessons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MLS is the data source, IDX is the display permission, and the feed (usually the RESO Web API) is the transport.&lt;/li&gt;
&lt;li&gt;Sort out licensing and feed type before estimating.&lt;/li&gt;
&lt;li&gt;Sync incrementally, store raw data, normalise to your own schema and serve from your own store.&lt;/li&gt;
&lt;li&gt;Plan for removals, photos, field quirks and display rules from day one.&lt;/li&gt;
&lt;li&gt;Approvals usually take longer than the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are planning a property platform and want the data layer scoped properly, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a call&lt;/a&gt; and we can walk through your MLS, your feed and your timeline.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/mls-idx-integration-real-estate-platforms" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>realestate</category>
      <category>api</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Is Web Scraping Legal? A Practical Guide for Businesses</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Tue, 13 Oct 2026 05:16:58 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/is-web-scraping-legal-a-practical-guide-for-businesses-2fjn</link>
      <guid>https://dev.to/aqib_javaid/is-web-scraping-legal-a-practical-guide-for-businesses-2fjn</guid>
      <description>&lt;p&gt;If you are asking &lt;strong&gt;is web scraping legal for business&lt;/strong&gt;, the short answer is: collecting publicly available data is often lawful, but "often" is doing a lot of work in that sentence. The risk depends on what you collect, how you collect it, which terms you have agreed to, and what you do with the data afterwards.&lt;/p&gt;

&lt;p&gt;I build scrapers and data pipelines for a living (see my &lt;a href="https://aqibjavaid.site/services/web-scraping-data-extraction" rel="noopener noreferrer"&gt;web scraping and data extraction service&lt;/a&gt;), so this is the practical version I walk clients through before a project starts. I am an engineer, not a lawyer, and nothing here is legal advice. Treat it as a map of the questions to ask your own counsel.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short answer: it depends on five things
&lt;/h2&gt;

&lt;p&gt;Scraping is not one legal question. It is five, and each can sink a project on its own:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is the data public, or behind a login?&lt;/strong&gt; Public pages carry far less risk than anything you can only reach by signing in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it personal data?&lt;/strong&gt; Names, emails, phone numbers and profiles trigger privacy law almost everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What do the site's terms say, and did you agree to them?&lt;/strong&gt; Accepting terms by creating an account is very different from visiting a public page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the content protected by copyright or database rights?&lt;/strong&gt; Facts are treated differently from creative text, images and structured databases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How hard are you hitting the server?&lt;/strong&gt; Scraping that degrades a site can create its own problems, however public the data is.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can answer all five, you can usually tell whether a project is low, medium or high risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Public data versus data behind a login
&lt;/h2&gt;

&lt;p&gt;In the United States, the main anti-hacking law people cite is the Computer Fraud and Abuse Act (CFAA). In &lt;em&gt;Van Buren v. United States&lt;/em&gt; (2021), the Supreme Court read its "exceeds authorized access" language narrowly, and later decisions, including the Ninth Circuit's &lt;em&gt;hiQ v. LinkedIn&lt;/em&gt;, leaned toward the view that scraping pages anyone can open is unlikely to count as "unauthorized access".&lt;/p&gt;

&lt;p&gt;That does not make public scraping a free-for-all. The &lt;em&gt;hiQ&lt;/em&gt; dispute ended in a settlement, and courts have kept looking at other claims such as breach of contract and interference with the site's systems.&lt;/p&gt;

&lt;p&gt;My working rule for clients is simple: &lt;strong&gt;public pages, no login, no circumventing technical blocks&lt;/strong&gt; is the lowest-risk category. The moment a scraper logs in, uses someone else's credentials, or defeats an access control, the risk jumps, because you are now acting under an account and its terms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Terms of service and contracts
&lt;/h2&gt;

&lt;p&gt;Terms of service are where many scraping disputes are really fought. A site's terms may forbid automated access, and a contract claim does not need a hacking law at all.&lt;/p&gt;

&lt;p&gt;Recent case law shows how fact-specific this is. In &lt;em&gt;Meta v. Bright Data&lt;/em&gt;, a federal court in California found in early 2024 that Meta's terms governed a user's "use" of its products, and that scraping public pages while logged out was not that kind of use. &lt;a href="https://www.fbm.com/technology/publications/major-decision-affects-law-of-scraping-and-online-data-collection-meta-platforms-v-bright-data/" rel="noopener noreferrer"&gt;Legal commentary on the decision&lt;/a&gt; is clear that this does not establish that all logged-off scraping is allowed. It turned on Meta's specific wording.&lt;/p&gt;

&lt;p&gt;The practical lesson:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Never scrape while logged in&lt;/strong&gt; to a service whose terms forbid it. By logging in you agreed to those terms.&lt;/li&gt;
&lt;li&gt;Read the terms of every source before you build, and keep a dated copy.&lt;/li&gt;
&lt;li&gt;If a source matters to the business, ask for permission or an API. It is often cheaper than a dispute.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Personal data: GDPR, CCPA and similar laws
&lt;/h2&gt;

&lt;p&gt;This is the area where I see businesses underestimate risk the most. Data being public does not make it free to use. Under the EU's &lt;a href="https://eur-lex.europa.eu/eli/reg/2016/679/oj" rel="noopener noreferrer"&gt;GDPR&lt;/a&gt;, personal data stays personal data even when it is published on a website. You still need a lawful basis, you must tell people how their data is used, and you must respect their rights. California's CCPA and several other US state laws add their own rules.&lt;/p&gt;

&lt;p&gt;Practical ways to reduce the exposure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't collect personal data you don't need.&lt;/strong&gt; If you only need prices, don't store seller names.&lt;/li&gt;
&lt;li&gt;Separate personal fields from the rest of the dataset, or drop them at ingestion.&lt;/li&gt;
&lt;li&gt;Document why you collect each field and how long you keep it.&lt;/li&gt;
&lt;li&gt;Be extra careful with sensitive categories and with scraping social profiles to build contact lists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your use case is lead generation from personal profiles, speak to a privacy lawyer before the first request goes out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copyright and database rights
&lt;/h2&gt;

&lt;p&gt;Facts, such as a price, a date or a record number, are generally not protected by copyright in the US. Creative expression is: article text, photos, reviews and descriptions. In the EU and UK, a separate database right can protect a substantial investment in collecting a database.&lt;/p&gt;

&lt;p&gt;So scraping product prices to analyse the market is a very different thing from copying a competitor's product descriptions and photos onto your own site, or from feeding a publisher's full articles into a product you sell.&lt;/p&gt;

&lt;p&gt;A simple filter: &lt;strong&gt;extract and use facts, don't republish creative content.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  robots.txt and rate limiting
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;robots.txt&lt;/code&gt; is the convention sites use to say which paths automated tools should avoid. The format is documented in &lt;a href="https://www.rfc-editor.org/rfc/rfc9309" rel="noopener noreferrer"&gt;RFC 9309&lt;/a&gt;. On its own it is a voluntary standard, not a law, but ignoring it looks bad if a dispute starts, and it is a good signal of what the site owner expects.&lt;/p&gt;

&lt;p&gt;I treat it as a rule of the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Respect &lt;code&gt;robots.txt&lt;/code&gt; and crawl-delay hints unless there is a clear reason not to, agreed with the client.&lt;/li&gt;
&lt;li&gt;Identify your scraper honestly where that is practical.&lt;/li&gt;
&lt;li&gt;Rate-limit so you never slow the site down for real users.&lt;/li&gt;
&lt;li&gt;Back off automatically when you get errors or 429 responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hammering a server is the fastest way to turn a grey area into a complaint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world example: public municipal data
&lt;/h2&gt;

&lt;p&gt;On the Violerts platform, which consolidates fragmented NYC municipal property data into one compliance tool, the work involved multi-agency data ingestion and asynchronous scraping. Public government sources are about as low-risk as scraping gets, because the data is published for public use. Even so, the engineering question was never just "can we fetch it?" It was how to collect the data reliably and politely, and how to keep the pipeline healthy when sources change, which I cover in &lt;a href="https://aqibjavaid.site/blog/scraped-data-change-detection" rel="noopener noreferrer"&gt;scraped data change detection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can read the full &lt;a href="https://aqibjavaid.site/work/violerts-nyc-proptech-compliance-saas" rel="noopener noreferrer"&gt;Violerts case study&lt;/a&gt; for how that system is structured. The same discipline applies in my write-up on &lt;a href="https://aqibjavaid.site/blog/resilient-web-scraping-system-government-data-sources" rel="noopener noreferrer"&gt;building a resilient scraper for government data sources&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical checklist before you scrape
&lt;/h2&gt;

&lt;p&gt;Before any scraping project starts, I ask the client to answer these questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What exact fields do we need, and why?&lt;/li&gt;
&lt;li&gt;Is the source public with no login required?&lt;/li&gt;
&lt;li&gt;Do the terms of service forbid automated access? Have we kept a copy?&lt;/li&gt;
&lt;li&gt;Does the dataset include personal data? If yes, what is the lawful basis?&lt;/li&gt;
&lt;li&gt;Is there an official API, data feed or licence we could use instead?&lt;/li&gt;
&lt;li&gt;Are we respecting &lt;code&gt;robots.txt&lt;/code&gt; and limiting request rates?&lt;/li&gt;
&lt;li&gt;How will we store, secure and delete the data?&lt;/li&gt;
&lt;li&gt;Who reviews the legal position, and when?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If question 5 has a yes, use the API. It is more stable, and it removes most of the legal grey area.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to get a lawyer involved
&lt;/h2&gt;

&lt;p&gt;Bring in a lawyer when any of these are true:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You plan to scrape behind a login or through an account&lt;/li&gt;
&lt;li&gt;The dataset includes personal data about EU, UK or California residents&lt;/li&gt;
&lt;li&gt;You will resell or republish the data as your product&lt;/li&gt;
&lt;li&gt;The source is a direct competitor or has already sent a cease-and-desist&lt;/li&gt;
&lt;li&gt;Your business depends on one source staying available&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For everything else, a clear written policy and a well-built scraper that behaves politely will take you most of the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Scraping public, non-personal data without logging in is usually the lowest-risk category, but "usually" is not "always".&lt;/li&gt;
&lt;li&gt;Terms of service, privacy law and copyright are where businesses get into trouble, not the act of fetching a page.&lt;/li&gt;
&lt;li&gt;Respect &lt;code&gt;robots.txt&lt;/code&gt;, rate-limit, and never defeat access controls.&lt;/li&gt;
&lt;li&gt;Collect only what you need, and drop personal data you don't.&lt;/li&gt;
&lt;li&gt;Prefer an official API or licence when one exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a scraping or data-extraction project scoped sensibly, with the compliance questions answered up front, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a call&lt;/a&gt; and we can go through your use case.&lt;/p&gt;

&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/is-web-scraping-legal-for-business" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>legal</category>
      <category>privacy</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build vs Buy an Event Booking System: How to Decide</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Mon, 12 Oct 2026 10:08:45 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/build-vs-buy-an-event-booking-system-how-to-decide-20fi</link>
      <guid>https://dev.to/aqib_javaid/build-vs-buy-an-event-booking-system-how-to-decide-20fi</guid>
      <description>&lt;p&gt;You run events, classes, tours or appointments, and people need a way to book and pay. Do you sign up for an existing tool, or pay someone to build a &lt;strong&gt;custom event booking system&lt;/strong&gt;? The honest answer is "buy first, build when the tool starts getting in your way", but the details decide whether that's right for you.&lt;/p&gt;

&lt;p&gt;Choosing wrong in either direction is expensive. Build too early and you spend months on features an existing tool already had. Buy too long and you end up patching around a platform that doesn't fit your business, paying fees on every booking and unable to change the parts that matter.&lt;/p&gt;

&lt;p&gt;This guide gives you a way to decide. It covers what off-the-shelf tools do well, the signs that you've outgrown them, what a custom build involves, and a middle path that often beats both. It comes from my &lt;a href="https://aqibjavaid.site/services/event-booking-platform-development" rel="noopener noreferrer"&gt;event and booking platform development work&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ready-made booking tools do well
&lt;/h2&gt;

&lt;p&gt;Existing tools such as ticketing platforms like &lt;a href="https://www.eventbrite.com" rel="noopener noreferrer"&gt;Eventbrite&lt;/a&gt;, scheduling tools like &lt;a href="https://calendly.com" rel="noopener noreferrer"&gt;Calendly&lt;/a&gt;, and booking plugins for WordPress cover the common cases quickly. Their strengths are real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed.&lt;/strong&gt; You can be taking bookings the same day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low upfront cost.&lt;/strong&gt; You pay monthly or per ticket instead of paying for development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance is someone else's job.&lt;/strong&gt; Security updates, payment compliance and bug fixes are handled for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Familiar to customers.&lt;/strong&gt; Many buyers already trust the checkout.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your needs are standard (one organiser, simple tickets or time slots, ordinary payments), buying is almost always the right call. Don't build what you can rent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signs you've outgrown an off-the-shelf tool
&lt;/h2&gt;

&lt;p&gt;Watch for these. One of them is an annoyance. Three or four is a pattern.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fees eat your margin.&lt;/strong&gt; Per-ticket or per-booking fees add up as volume grows, and they apply to money you could keep.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your workflow doesn't fit.&lt;/strong&gt; You need approval steps, custom pricing, packages, deposits, multi-day bookings or resource limits the tool can't model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You run a marketplace.&lt;/strong&gt; Several organisers or vendors, each with their own listings, payouts and messages, is a different product from a single-organiser tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need your own brand and flow.&lt;/strong&gt; The checkout, emails and pages must look and behave like your business, not the vendor's.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration is awkward.&lt;/strong&gt; Your CRM, accounting, calendar or inventory can't connect cleanly, so staff copy data by hand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need multiple languages or regions.&lt;/strong&gt; Different currencies, languages and local rules are hard to bolt on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You own the data question.&lt;/strong&gt; You need reporting, exports or data ownership the platform won't give you.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What a custom booking system really involves
&lt;/h2&gt;

&lt;p&gt;A booking system looks like a calendar and a pay button. The complexity sits behind them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Availability logic.&lt;/strong&gt; Capacity, time zones, overlapping bookings, cut-off times and cancellations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments.&lt;/strong&gt; Deposits, refunds, split payouts and failure handling. If you take money for other organisers, a service like &lt;a href="https://stripe.com/docs/connect" rel="noopener noreferrer"&gt;Stripe Connect&lt;/a&gt; is designed for marketplace payouts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notifications.&lt;/strong&gt; Confirmations, reminders and messages that arrive reliably.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Admin tools.&lt;/strong&gt; Staff need to create events, manage bookings, handle exceptions and see reports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content and SEO.&lt;/strong&gt; Event pages that load fast and can be found on search engines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time updates.&lt;/strong&gt; Live availability and messaging so two people don't book the last seat.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the &lt;a href="https://aqibjavaid.site/work/planuojam-marketplace-case-study" rel="noopener noreferrer"&gt;Planuojam event marketplace&lt;/a&gt;, the work involved rescuing a failing event platform by architecting a headless marketplace supporting five languages, real-time messaging and Stripe payments. It also included a custom AWS Translate-powered engine in Strapi 5 to automate nested content localisation while preserving SEO integrity. That list is a good illustration of how quickly "event booking" grows once you add languages, messaging and payments.&lt;/p&gt;

&lt;h2&gt;
  
  
  The middle path: buy the core, build the edges
&lt;/h2&gt;

&lt;p&gt;You don't have to choose all or nothing. A common and sensible approach is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep a proven tool for the parts that are the same for everyone, such as payment processing.&lt;/li&gt;
&lt;li&gt;Build custom pieces only where your business is different, such as your pricing rules, your booking flow or your dashboard.&lt;/li&gt;
&lt;li&gt;Connect the two through APIs, so you can replace either side later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps the upfront cost down and avoids a full rebuild. The same approach applies to other platforms, as I discussed in the &lt;a href="https://aqibjavaid.site/blog/shopify-vs-custom-ecommerce" rel="noopener noreferrer"&gt;Shopify versus custom ecommerce comparison&lt;/a&gt;. If you go this way, plan the integrations carefully. The &lt;a href="https://aqibjavaid.site/blog/api-integration-cost-timeline" rel="noopener noreferrer"&gt;API integration cost guide&lt;/a&gt; explains what drives the price.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to compare the real cost
&lt;/h2&gt;

&lt;p&gt;Don't compare the monthly fee with the build price. Compare total cost over three years.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Buy&lt;/th&gt;
&lt;th&gt;Build&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Upfront&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Higher (development)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly or per booking&lt;/td&gt;
&lt;td&gt;Subscription plus per-ticket fees&lt;/td&gt;
&lt;td&gt;Hosting and payment processing fees&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changes and new features&lt;/td&gt;
&lt;td&gt;Limited by the vendor&lt;/td&gt;
&lt;td&gt;Paid development, but you decide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;Ongoing, small but real&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Risk&lt;/td&gt;
&lt;td&gt;Price rises, feature changes, vendor lock-in&lt;/td&gt;
&lt;td&gt;Scope creep, build quality&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Add up your expected bookings per year and multiply by the platform's per-booking fee. That number, over three years, is the figure to set against a build quote. For general budgeting of a custom product, the &lt;a href="https://aqibjavaid.site/blog/custom-web-app-mvp-cost-2026" rel="noopener noreferrer"&gt;custom web app MVP cost guide&lt;/a&gt; gives realistic tiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to build
&lt;/h2&gt;

&lt;p&gt;Skip a custom build when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You haven't yet proven that people will book. Validate with a simple tool first.&lt;/li&gt;
&lt;li&gt;Your needs fit a standard tool with a little configuration.&lt;/li&gt;
&lt;li&gt;You can't commit to maintaining it after launch.&lt;/li&gt;
&lt;li&gt;You'd be rebuilding features that make up 90% of what the tool already does well.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A simple way to decide
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Can a ready-made tool run your events or bookings today with no workarounds? &lt;strong&gt;Buy it.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Are fees or workarounds already costing you real money each month? &lt;strong&gt;Cost out a build or a hybrid.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Is your model a marketplace, or does it need multiple languages, special pricing or deep integrations? &lt;strong&gt;Lean toward building.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Unsure? &lt;strong&gt;Start with a tool, track what you work around, and revisit in three to six months.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much does a custom event booking system cost?
&lt;/h3&gt;

&lt;p&gt;It depends on the scope: roles, payment model, languages and integrations. A simple booking flow is far cheaper than a multi-vendor marketplace. Get a scoped estimate from the features you need, not a flat number.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I start with a tool and move to a custom system later?
&lt;/h3&gt;

&lt;p&gt;Yes, and it's often the smartest path. Keep your customer and booking data exportable, and move over when you've learned what you actually need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is a WordPress booking plugin enough?
&lt;/h3&gt;

&lt;p&gt;For a single organiser with simple needs, often yes. When you need marketplace features, complex rules or high volume, plugins start to strain.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the biggest hidden cost of building?
&lt;/h3&gt;

&lt;p&gt;Maintenance and change. A booking system is never finished, because rules, payments and integrations keep moving. Budget for ongoing work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Buy first. Build when fees, workflow limits or a marketplace model make the tool a poor fit.&lt;/li&gt;
&lt;li&gt;Compare total cost over three years, including per-booking fees.&lt;/li&gt;
&lt;li&gt;A hybrid, buying the standard parts and building the differences, often wins.&lt;/li&gt;
&lt;li&gt;A booking system is more than a calendar: availability, payments, notifications and admin tools carry the complexity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're weighing a booking platform and want a senior engineer to give you an honest build-or-buy recommendation, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a free call&lt;/a&gt; and we'll go through your case together.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/custom-event-booking-system" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>startup</category>
      <category>webdev</category>
      <category>saas</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Custom API Integration Cost and Timeline: What to Expect</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Sun, 11 Oct 2026 05:01:45 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/custom-api-integration-cost-and-timeline-what-to-expect-2ln4</link>
      <guid>https://dev.to/aqib_javaid/custom-api-integration-cost-and-timeline-what-to-expect-2ln4</guid>
      <description>&lt;p&gt;"How much will it cost to connect our system to theirs?" is the question I hear most before an integration project, and it's the hardest one to answer from a one-line description. A &lt;strong&gt;custom API integration cost&lt;/strong&gt; can be a few days of work or several months, and both can be described with the same sentence: "we need to connect X to Y".&lt;/p&gt;

&lt;p&gt;The reason is that the API call is the easy part. What takes time is everything around it: how the data maps between two systems that describe the same thing differently, what happens when a request fails, how you know a payment really went through, and who gets alerted at 2 a.m. when it doesn't.&lt;/p&gt;

&lt;p&gt;This guide breaks down what drives the cost and the timeline, gives typical effort ranges by complexity, and shows what to ask a developer before you agree on a price. It draws on the kind of integration work I do through my &lt;a href="https://aqibjavaid.site/services/api-development-integrations" rel="noopener noreferrer"&gt;API development and integrations service&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
The hour ranges below are typical planning figures from my experience, not a quote. Your price is the hours multiplied by the hourly or project rate of whoever builds it, and rates vary widely by region and seniority.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What you are actually paying for
&lt;/h2&gt;

&lt;p&gt;An integration has more layers than "call the API". When I scope one, I look at these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Authentication.&lt;/strong&gt; API keys are quick. OAuth, token refresh and per-customer connections take longer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data mapping.&lt;/strong&gt; Field names, formats, units and required fields almost never match between two systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business rules.&lt;/strong&gt; Which records sync, in which direction, and what wins when both sides change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling and retries.&lt;/strong&gt; Timeouts, rate limits and temporary failures need a plan, not a hope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhooks and status sync.&lt;/strong&gt; Many services tell you about events after the fact, and those messages must be verified and processed safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing and monitoring.&lt;/strong&gt; Sandbox accounts, edge cases, logging, and alerts when something breaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation and handover.&lt;/strong&gt; Someone has to be able to maintain it after the builder leaves.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Cheap quotes usually cover the first two layers and skip the rest. Those skipped layers are where integrations fail six months later, which I wrote about in the &lt;a href="https://aqibjavaid.site/blog/third-party-api-integration-best-practices" rel="noopener noreferrer"&gt;guide to third-party API integration best practices&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typical effort by complexity
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type of integration&lt;/th&gt;
&lt;th&gt;Typical effort&lt;/th&gt;
&lt;th&gt;Typical calendar time&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple, one-way&lt;/td&gt;
&lt;td&gt;15 to 30 hours&lt;/td&gt;
&lt;td&gt;About 1 week&lt;/td&gt;
&lt;td&gt;Push new form submissions into a CRM or spreadsheet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard, two-way with events&lt;/td&gt;
&lt;td&gt;20 to 60 hours&lt;/td&gt;
&lt;td&gt;1 to 3 weeks&lt;/td&gt;
&lt;td&gt;Payments plus webhooks, or a CRM sync with status updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex, several systems&lt;/td&gt;
&lt;td&gt;60 to 150+ hours&lt;/td&gt;
&lt;td&gt;4 to 10+ weeks&lt;/td&gt;
&lt;td&gt;Multiple carriers, ERP data, approval rules and reporting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Treat the ranges as starting points. The same integration can fall at either end depending on four things: how good the third party's documentation is, whether a sandbox exists, how messy your existing data is, and how strict your reliability needs are.&lt;/p&gt;

&lt;p&gt;To turn hours into money, multiply by the rate. As a simple illustration, a standard 40-hour integration at a $50 hourly rate would be about $2,000, and at $100 an hour about $4,000. Your actual rate will differ, and a fixed-price quote should still tell you which of the seven layers above it covers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What pushes the cost up
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Poor or outdated documentation.&lt;/strong&gt; Reverse-engineering behaviour costs hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limits and quotas.&lt;/strong&gt; You need queues, batching and back-off logic instead of a simple loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Many third parties at once.&lt;/strong&gt; On the &lt;a href="https://aqibjavaid.site/work/multi-carrier-bulk-sim-activation-platform" rel="noopener noreferrer"&gt;multi-carrier SIM activation platform&lt;/a&gt;, activations had to be routed across a dozen-plus carrier and MVNO backends, which meant per-carrier logic, asynchronous processing with retries and live progress for the user. Each extra system adds mapping and testing, not just one more endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Money involved.&lt;/strong&gt; Payment flows demand idempotency, verified webhooks and careful reconciliation. A duplicate charge costs far more than the extra hours of safety work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time requirements.&lt;/strong&gt; Live rates and instant status are harder than nightly syncs. On &lt;a href="https://aqibjavaid.site/work/expreco-logistics-quote-system" rel="noopener noreferrer"&gt;EXPRECO&lt;/a&gt;, the platform integrated UPS and FedEx APIs to calculate real-time shipping rates from package details and destination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dirty source data.&lt;/strong&gt; If your records contain duplicates and gaps, cleaning them can cost as much as the integration. The &lt;a href="https://aqibjavaid.site/blog/crm-migration-duplicate-contacts-data-cleanup" rel="noopener noreferrer"&gt;CRM migration cleanup case&lt;/a&gt; shows how that plays out.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What keeps the cost down
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Choose an API with good docs, a sandbox and official SDKs.&lt;/li&gt;
&lt;li&gt;Decide the data rules before development starts: what syncs, which way, and which side wins.&lt;/li&gt;
&lt;li&gt;Start with one flow end to end, then add more. A thin working slice beats a half-built everything.&lt;/li&gt;
&lt;li&gt;Ask for retries, logging and alerts in the scope. They're cheaper to add now than after the first outage.&lt;/li&gt;
&lt;li&gt;Use proven building blocks. For payments, Stripe's own guidance on &lt;a href="https://stripe.com/docs/webhooks" rel="noopener noreferrer"&gt;webhooks&lt;/a&gt; and &lt;a href="https://stripe.com/docs/api/idempotent_requests" rel="noopener noreferrer"&gt;idempotent requests&lt;/a&gt; describes the safety patterns your developer should follow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A real example: payments synced with a CRM
&lt;/h2&gt;

&lt;p&gt;On the &lt;a href="https://aqibjavaid.site/work/stripe-lead-billing-gohighlevel" rel="noopener noreferrer"&gt;Stripe lead billing project&lt;/a&gt;, the workflow let lead-generation clients save a payment method without an immediate charge, then automatically charged it when qualified leads were delivered. The payment lifecycle was connected to GoHighLevel, with webhook-driven status synchronisation for successful and failed payments.&lt;/p&gt;

&lt;p&gt;On paper that sounds like "Stripe plus a CRM". The parts that need care are the ones around the call: saving payment methods safely, reacting to success and failure events, and keeping both systems in agreement. That's why I scope by layers rather than by endpoint count. If you want the technical side of making retries safe, the &lt;a href="https://aqibjavaid.site/blog/laravel-api-idempotency-keys" rel="noopener noreferrer"&gt;idempotency keys article&lt;/a&gt; and the &lt;a href="https://aqibjavaid.site/blog/laravel-webhook-delivery-architecture" rel="noopener noreferrer"&gt;webhook delivery guide&lt;/a&gt; go into detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Questions to ask before you agree on a price
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Which of the seven layers does this quote cover, and which does it leave out?&lt;/li&gt;
&lt;li&gt;How are failures handled, and who is told when one happens?&lt;/li&gt;
&lt;li&gt;Is there a sandbox, and will the integration be tested against it?&lt;/li&gt;
&lt;li&gt;What happens when the third party changes or retires its API?&lt;/li&gt;
&lt;li&gt;Will I get documentation and the code, so I'm not locked in?&lt;/li&gt;
&lt;li&gt;What's the ongoing cost? Integrations need small amounts of maintenance each year.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  When a ready-made connector is the better choice
&lt;/h2&gt;

&lt;p&gt;Not every integration needs to be custom. If a no-code tool or a vendor's built-in connector already does what you need, use it. Custom work earns its cost when you need special business rules, reliable handling of money or high volume, or when no connector exists. For a related build-or-buy view on a bigger scale, see the &lt;a href="https://aqibjavaid.site/blog/custom-crm-development-cost" rel="noopener noreferrer"&gt;custom CRM cost guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How long does an API integration take?
&lt;/h3&gt;

&lt;p&gt;A simple one-way integration can take about a week. A standard two-way integration usually takes one to three weeks, and multi-system work can take a couple of months. The documentation and your data quality are the biggest variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is a custom API integration so expensive?
&lt;/h3&gt;

&lt;p&gt;The API call is cheap. The cost is in mapping, error handling, security, testing and monitoring, which keep the integration working after launch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I pay for maintenance after launch?
&lt;/h3&gt;

&lt;p&gt;Usually a little. Third parties change their APIs, keys expire and edge cases appear. Budget for light maintenance, and ask what is included.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I get a fixed price?
&lt;/h3&gt;

&lt;p&gt;Yes, if the scope is clear. Provide the systems, the data rules and the failure handling you expect, and a good developer can price it. Vague scope leads to vague prices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The cost of an integration is in the layers around the API call, not the call itself.&lt;/li&gt;
&lt;li&gt;Typical planning ranges are about 15 to 30 hours for simple, 20 to 60 for standard and 60 to 150+ for complex integrations.&lt;/li&gt;
&lt;li&gt;Documentation quality, rate limits, data quality and money flows are the biggest cost drivers.&lt;/li&gt;
&lt;li&gt;Ask what a quote covers: errors, retries, monitoring and handover all matter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're planning an integration and want a clear, layered estimate from a senior engineer, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a free call&lt;/a&gt; and we'll scope it together.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/api-integration-cost-timeline" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>startup</category>
      <category>backend</category>
    </item>
    <item>
      <title>React Native vs Flutter for a Startup MVP: How to Choose</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Sat, 10 Oct 2026 07:50:00 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/react-native-vs-flutter-for-a-startup-mvp-how-to-choose-1peh</link>
      <guid>https://dev.to/aqib_javaid/react-native-vs-flutter-for-a-startup-mvp-how-to-choose-1peh</guid>
      <description>&lt;p&gt;You want a mobile app on both iPhone and Android, and your budget says you can't build two separate native apps. So the question lands on your desk: &lt;strong&gt;React Native vs Flutter for an MVP&lt;/strong&gt;. Developers will argue about it endlessly, and most of that argument won't help you decide.&lt;/p&gt;

&lt;p&gt;Both can ship a good first version of your product. Both let one team build for iOS and Android from a single codebase. The difference that matters to a founder is rarely the technology itself. It's who you can hire, what you already own, and what your app needs to do on the phone.&lt;/p&gt;

&lt;p&gt;This guide gives you a decision you can defend. I'll explain what each framework is in plain terms, where each one genuinely wins, and how I'd choose for a startup MVP. It comes from my &lt;a href="https://aqibjavaid.site/services/mobile-app-development" rel="noopener noreferrer"&gt;mobile app development work&lt;/a&gt; with both frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What React Native and Flutter actually are
&lt;/h2&gt;

&lt;p&gt;Both are cross-platform frameworks: you write the app once and ship it to iOS and Android. They get there in different ways.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://reactnative.dev" rel="noopener noreferrer"&gt;React Native&lt;/a&gt;&lt;/strong&gt; is built on JavaScript or TypeScript and React. Your screens are described in React components and rendered as real native interface elements on each platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.flutter.dev" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt;&lt;/strong&gt; is built by Google on the Dart language. It draws every pixel itself with its own rendering engine, so the app looks the same on every device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That one difference explains most of what follows. React Native feels more like the platform it runs on. Flutter feels more like a single, consistent design system that you control completely.&lt;/p&gt;

&lt;h2&gt;
  
  
  The decision that matters most: your team
&lt;/h2&gt;

&lt;p&gt;For an MVP, the best framework is usually the one your team can already use well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your web team writes React and TypeScript, React Native lets them reuse skills, tools and sometimes code such as validation, API clients and business logic. The user interface code is not shared one-for-one with the web, but the thinking is.&lt;/li&gt;
&lt;li&gt;If you are hiring from scratch, JavaScript and TypeScript developers are easier to find than Dart developers in most markets. A bigger pool usually means shorter hiring and easier replacements.&lt;/li&gt;
&lt;li&gt;If you have, or can hire, a developer who is strong in Flutter, don't switch frameworks just to follow a trend. A skilled Flutter developer will ship faster than a mediocre React Native one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the same logic I used when comparing backends in the &lt;a href="https://aqibjavaid.site/blog/laravel-vs-nodejs-for-saas" rel="noopener noreferrer"&gt;Laravel vs Node.js guide&lt;/a&gt;: the hiring pool and the team's current strength often outweigh small technical differences.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where React Native tends to win
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You already have React developers or a React web app.&lt;/strong&gt; The learning curve is small.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want the app to look and behave like each platform.&lt;/strong&gt; Because it uses native components, system behaviour such as scrolling, text input and accessibility follows the platform's own rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need a large ecosystem.&lt;/strong&gt; There is a wide range of libraries, and most services publish a JavaScript SDK first. For a faster start, the React Native documentation points new projects to a framework such as &lt;a href="https://docs.expo.dev" rel="noopener noreferrer"&gt;Expo&lt;/a&gt;, which handles a lot of setup for you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Over-the-air updates matter.&lt;/strong&gt; The JavaScript layer can often be updated without a full store release, within the store rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where Flutter tends to win
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You want a highly custom, branded interface.&lt;/strong&gt; Because Flutter draws everything itself, animations and unusual layouts look identical on every device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want predictable behaviour across devices.&lt;/strong&gt; There are fewer surprises between Android versions and phone models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need one codebase for more than phones.&lt;/strong&gt; Flutter also targets web and desktop, although its web support suits app-like products better than content sites that depend on search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex, animation-heavy screens are the product.&lt;/strong&gt; Maps, live tracking and custom dashboards are comfortable territory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I've built: a Flutter example
&lt;/h2&gt;

&lt;p&gt;On the &lt;a href="https://aqibjavaid.site/work/ion-dash-logistics-delivery-fleet-management" rel="noopener noreferrer"&gt;ION DASH logistics platform&lt;/a&gt;, I worked with Flutter customer and driver apps connected to a Laravel backend API. The platform covers custom quotes, automated dispatch, background GPS tracking, delivery confirmation, proof of delivery, Stripe PaymentIntents, Firebase Cloud Messaging and Google Maps navigation.&lt;/p&gt;

&lt;p&gt;That project shows a point that matters more than the framework: most of the risk in a mobile product lives in the backend and the integrations around it, such as payments, push notifications, maps and background location. The app screens are the visible part. The hard part is making the whole system behave reliably when a driver's phone loses signal. Whichever framework you pick, plan the API and the integrations first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What affects the cost of a mobile MVP
&lt;/h2&gt;

&lt;p&gt;Framework choice rarely changes the budget as much as these do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The number of screens and user roles.&lt;/strong&gt; A customer app and a driver app are two products, not one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations.&lt;/strong&gt; Payments, maps, push notifications, login providers and third-party APIs each add work and testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background features.&lt;/strong&gt; Location tracking, offline mode and background sync are more complex than ordinary screens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store requirements.&lt;/strong&gt; Apple and Google review processes, privacy declarations and testing on real devices take time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The backend.&lt;/strong&gt; Most apps need an API, an admin panel and a database before the first screen is useful.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For general MVP budgeting, I covered realistic price tiers in the &lt;a href="https://aqibjavaid.site/blog/custom-web-app-mvp-cost-2026" rel="noopener noreferrer"&gt;custom web app MVP cost guide&lt;/a&gt;, and the early technical decisions that save money later are in the &lt;a href="https://aqibjavaid.site/blog/technical-decisions-before-building-saas-product" rel="noopener noreferrer"&gt;SaaS planning checklist&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When neither is the right answer
&lt;/h2&gt;

&lt;p&gt;Cross-platform isn't always correct. Consider going native, or skipping the app, when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your product depends on deep, cutting-edge platform features, such as advanced camera, augmented reality or heavy hardware integration.&lt;/li&gt;
&lt;li&gt;You only need one platform to prove the idea.&lt;/li&gt;
&lt;li&gt;A responsive web app or a progressive web app would test your idea faster and cheaper. Many MVPs don't need the app stores at all yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Starting with a web product and adding the mobile app after you've confirmed demand is a perfectly good strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple way to decide
&lt;/h2&gt;

&lt;p&gt;Answer these in order and stop at the first clear result:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do you already have a team with strong skills in one of them? &lt;strong&gt;Use that one.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Is your web product built in React, and do you want to share thinking and tools? &lt;strong&gt;React Native.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Is a custom, animated, identical-everywhere interface central to the product? &lt;strong&gt;Flutter.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Do you need to hire quickly in a market where JavaScript developers are much easier to find? &lt;strong&gt;React Native.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Still unclear? &lt;strong&gt;Build the single riskiest screen in both for a day and compare.&lt;/strong&gt; A small spike is cheaper than the wrong choice.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is React Native or Flutter better for a startup MVP?
&lt;/h3&gt;

&lt;p&gt;Neither is better in general. React Native often wins on hiring and web skill reuse, and Flutter often wins on custom visuals and consistency. Pick based on your team and your product.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Flutter faster than React Native?
&lt;/h3&gt;

&lt;p&gt;Both are fast enough for the vast majority of MVPs. Performance rarely decides the choice. Heavy animation and complex lists need care in either framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I switch frameworks later?
&lt;/h3&gt;

&lt;p&gt;Moving between them means rewriting the app screens, although your backend and API stay the same. That's one more reason to keep business logic in the API rather than in the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does a cross-platform MVP take?
&lt;/h3&gt;

&lt;p&gt;It depends on the scope: roles, integrations and background features. A focused first version with a small set of features is realistic in weeks, not days, and the backend usually takes as much effort as the screens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Both frameworks can ship a good MVP, so decide on team, hiring and product needs.&lt;/li&gt;
&lt;li&gt;React Native suits React teams and platform-native behaviour. Flutter suits custom, consistent interfaces.&lt;/li&gt;
&lt;li&gt;The backend and integrations carry most of the risk and cost, so plan them first.&lt;/li&gt;
&lt;li&gt;A web app or PWA is sometimes the better first step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're planning a mobile MVP and want a senior engineer to help you choose and scope it, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a free call&lt;/a&gt; and we'll work through it together.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/react-native-vs-flutter-for-mvp" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>flutter</category>
      <category>mobile</category>
      <category>startup</category>
    </item>
    <item>
      <title>Laravel Hosting Compared: Forge, AWS and DigitalOcean Cost</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:40:25 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/laravel-hosting-compared-forge-aws-and-digitalocean-cost-fhp</link>
      <guid>https://dev.to/aqib_javaid/laravel-hosting-compared-forge-aws-and-digitalocean-cost-fhp</guid>
      <description>&lt;p&gt;Choosing where to host a Laravel app feels like a technical decision, but it's mostly a business one. Pick too small and the app falls over on your best sales day. Pick too big or too clever and you pay for capacity and complexity you don't use, every month, for years. This guide compares the main &lt;strong&gt;Laravel hosting options and their cost&lt;/strong&gt; so you can pick one with your eyes open.&lt;/p&gt;

&lt;p&gt;I'm not going to crown a winner, because there isn't one. The right answer depends on your traffic, your team and how much server work you want to own. What I can do, drawing on my &lt;a href="https://aqibjavaid.site/services/cloud-devops-deployment" rel="noopener noreferrer"&gt;cloud and DevOps deployment work&lt;/a&gt;, is show how each option works, what it really costs beyond the headline price, and which kind of project each one suits.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!NOTE]&lt;br&gt;
Prices below are rough, based on the providers' public pricing pages as I last checked them, and they change. Always confirm on the provider's own page before you budget.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The main ways to host a Laravel app
&lt;/h2&gt;

&lt;p&gt;There are really five categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Shared hosting.&lt;/strong&gt; Cheapest, but limited. Many plans can't run queue workers or give you SSH, which a serious Laravel app needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A VPS you manage yourself.&lt;/strong&gt; A virtual server from DigitalOcean, Hetzner or similar. Cheap and flexible, but you own security, updates and backups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A VPS managed through a tool.&lt;/strong&gt; &lt;a href="https://forge.laravel.com" rel="noopener noreferrer"&gt;Laravel Forge&lt;/a&gt; provisions and manages servers that you rent from a provider. You pay Forge a subscription plus the provider for the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A managed Laravel platform.&lt;/strong&gt; &lt;a href="https://cloud.laravel.com" rel="noopener noreferrer"&gt;Laravel Cloud&lt;/a&gt; runs your app for you, with a base plan plus usage-based charges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS with real infrastructure.&lt;/strong&gt; EC2, load balancers, managed databases and queues, or serverless through &lt;a href="https://vapor.laravel.com" rel="noopener noreferrer"&gt;Laravel Vapor&lt;/a&gt;. Powerful, and the most work to run well.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What each option costs in practice
&lt;/h2&gt;

&lt;p&gt;The monthly price on the homepage is rarely the full bill. Here is what to look at for each one.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Typical starting cost&lt;/th&gt;
&lt;th&gt;What you pay extra for&lt;/th&gt;
&lt;th&gt;You still own&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shared hosting&lt;/td&gt;
&lt;td&gt;Very low&lt;/td&gt;
&lt;td&gt;Little&lt;/td&gt;
&lt;td&gt;Almost nothing, but limits are tight&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DigitalOcean droplet&lt;/td&gt;
&lt;td&gt;From a few dollars a month for the smallest sizes, roughly $12 to $24 for a size that suits a small production app&lt;/td&gt;
&lt;td&gt;Backups, managed database, load balancer&lt;/td&gt;
&lt;td&gt;Server security, updates, deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forge on a VPS&lt;/td&gt;
&lt;td&gt;Forge subscription (from roughly $12 a month) plus the server&lt;/td&gt;
&lt;td&gt;The server, database and backups&lt;/td&gt;
&lt;td&gt;Server choices, monitoring, scaling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laravel Cloud&lt;/td&gt;
&lt;td&gt;A base plan from roughly $5 a month plus usage&lt;/td&gt;
&lt;td&gt;Compute, database and bandwidth as you use them&lt;/td&gt;
&lt;td&gt;Mostly the application itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS (EC2, RDS, etc.)&lt;/td&gt;
&lt;td&gt;Low for a single small instance, grows quickly with a load balancer, managed database and queues&lt;/td&gt;
&lt;td&gt;Nearly every component&lt;/td&gt;
&lt;td&gt;Architecture, security, cost control&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are ballpark figures taken from &lt;a href="https://www.digitalocean.com/pricing/droplets" rel="noopener noreferrer"&gt;DigitalOcean's droplet pricing&lt;/a&gt;, the Forge and Laravel Cloud pricing pages and &lt;a href="https://aws.amazon.com/lightsail/pricing/" rel="noopener noreferrer"&gt;AWS Lightsail pricing&lt;/a&gt;, and they will have moved by the time you read this.&lt;/p&gt;

&lt;p&gt;The number that surprises most founders isn't the server. It's the hidden extras: backups (often a percentage of the server price), a managed database, a staging environment, monitoring, and an hour of an engineer's time every time something needs attention. A cheap server that costs your team a day a month in maintenance isn't cheap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one fits which project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A small app or an MVP
&lt;/h3&gt;

&lt;p&gt;Start simple. A single small VPS managed with Forge, or a small Laravel Cloud plan, is usually enough. You get deployments, SSL, a database and queue workers without building infrastructure. Don't build for ten times your current traffic.&lt;/p&gt;

&lt;h3&gt;
  
  
  A growing SaaS with background jobs
&lt;/h3&gt;

&lt;p&gt;At this stage, the things that matter are queue workers, a separate database, scheduled jobs and zero-downtime deployments. Forge with two or three servers, or Laravel Cloud, handles this well. I covered the deployment side in the &lt;a href="https://aqibjavaid.site/blog/laravel-zero-downtime-deployments" rel="noopener noreferrer"&gt;zero-downtime deployments guide&lt;/a&gt; and the job side in the &lt;a href="https://aqibjavaid.site/blog/laravel-queue-architecture-saas" rel="noopener noreferrer"&gt;queue architecture article&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Heavy or spiky traffic, or compliance needs
&lt;/h3&gt;

&lt;p&gt;This is where AWS earns its complexity: autoscaling, managed databases, private networking and audit trails. When I worked on the &lt;a href="https://aqibjavaid.site/work/violerts-nyc-proptech-compliance-saas" rel="noopener noreferrer"&gt;Violerts compliance platform&lt;/a&gt;, the stack included Laravel Horizon, Redis, Nginx, Linux, AWS and CI/CD. That level of setup makes sense for a data-heavy SaaS. It would be overkill for a brochure app.&lt;/p&gt;

&lt;h3&gt;
  
  
  A team without a server person
&lt;/h3&gt;

&lt;p&gt;If nobody on your team wants to patch servers at 2 a.m., pay for a managed platform. The monthly premium is smaller than one bad weekend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forge vs AWS vs DigitalOcean: the honest comparison
&lt;/h2&gt;

&lt;p&gt;These three are the ones people ask about most, but they aren't direct competitors. DigitalOcean and AWS provide the servers. Forge is the tool that sets those servers up for you. You can use Forge on top of either provider, or even on a server you already rent elsewhere.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choose DigitalOcean (with Forge) when&lt;/strong&gt; you want predictable pricing, simple infrastructure and a clear bill. It is a sensible default for most small and mid-size Laravel apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose AWS when&lt;/strong&gt; you need services you can't get elsewhere, such as fine-grained networking, specific compliance controls or serious autoscaling, and you have someone who can run it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose Forge when&lt;/strong&gt; you want the speed of managed deployments without giving up control of your servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the &lt;a href="https://aqibjavaid.site/work/henceforward-wordpress-migration-security-optimization" rel="noopener noreferrer"&gt;Henceforward migration&lt;/a&gt;, the work involved moving a site onto Scaleway with Laravel Forge, along with security hardening and production tuning. It's a good example of the pattern: a managed tool on top of ordinary servers, chosen for a clean and maintainable setup rather than for scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to choose without regretting it
&lt;/h2&gt;

&lt;p&gt;Ask these five questions in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What breaks if the site is down for an hour?&lt;/strong&gt; If the answer is "a lot", budget for redundancy and monitoring from day one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who will maintain it?&lt;/strong&gt; If it's nobody, pick a managed option.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How spiky is the traffic?&lt;/strong&gt; Steady traffic suits a fixed server. Wild spikes suit autoscaling or serverless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where does the data have to live?&lt;/strong&gt; Regulation or customer contracts can fix the region and the provider.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How will you deploy and roll back?&lt;/strong&gt; Choose hosting that makes this a button, not a ritual.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're already on a setup that has grown messy, the lesson from &lt;a href="https://aqibjavaid.site/blog/stabilizing-aws-production-environment-devops" rel="noopener noreferrer"&gt;stabilising an AWS production environment&lt;/a&gt; applies: fix the foundations before you add more servers. That review is where I start on any hosting engagement.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to move your hosting
&lt;/h2&gt;

&lt;p&gt;Migrating hosts is risky and rarely fixes a slow app. If your pages are slow, look at the queries and the code before you throw bigger servers at them. I walked through that in the guide to &lt;a href="https://aqibjavaid.site/blog/laravel-slow-queries-fix" rel="noopener noreferrer"&gt;finding slow MySQL queries in Laravel&lt;/a&gt;. Move hosting when you've outgrown your current setup or you can't get the features you need, not because a different logo promises speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How much does it cost to host a Laravel app?
&lt;/h3&gt;

&lt;p&gt;A small production app can run on a single server for tens of dollars a month. Costs rise with traffic, databases, staging environments and backups. The bigger cost is usually engineering time, not the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Forge a hosting provider?
&lt;/h3&gt;

&lt;p&gt;No. Forge is a server management tool. You connect your own account at a provider such as DigitalOcean or AWS, and Forge sets up and deploys to the servers you rent there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Laravel Cloud better than Forge?
&lt;/h3&gt;

&lt;p&gt;They suit different people. Cloud takes more of the infrastructure off your hands and charges for usage. Forge gives you more control over the servers at a flat subscription price plus the server cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need AWS for a serious Laravel app?
&lt;/h3&gt;

&lt;p&gt;Not usually. Many busy Laravel apps run well on a few well-sized servers. Choose AWS when you need what it uniquely offers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The headline server price is only part of the bill. Count backups, databases, staging and maintenance time.&lt;/li&gt;
&lt;li&gt;DigitalOcean and AWS provide servers, Forge manages them, and Laravel Cloud is a managed platform. They aren't all the same kind of thing.&lt;/li&gt;
&lt;li&gt;Start small and simple, and move up when traffic or compliance requires it.&lt;/li&gt;
&lt;li&gt;Fix performance in the code and database before you spend more on servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're deciding where to host a Laravel app, or you suspect you're overpaying for your current setup, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a free call&lt;/a&gt; and I'll help you choose.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/laravel-hosting-options-cost" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>devops</category>
      <category>aws</category>
      <category>hosting</category>
    </item>
    <item>
      <title>Why WooCommerce Admin Is Slow and How to Fix It</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Thu, 08 Oct 2026 13:56:45 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/why-woocommerce-admin-is-slow-and-how-to-fix-it-4pjf</link>
      <guid>https://dev.to/aqib_javaid/why-woocommerce-admin-is-slow-and-how-to-fix-it-4pjf</guid>
      <description>&lt;p&gt;Your WooCommerce store looks fine to customers, but the admin has become painful. The Orders screen takes ten seconds to load, saving a product hangs, and the dashboard spins every time you open it. If you've been searching for a &lt;strong&gt;WooCommerce slow admin fix&lt;/strong&gt;, the good news is that the admin is usually slow for a small number of identifiable reasons, and most of them are fixable without rebuilding the store.&lt;/p&gt;

&lt;p&gt;A slow admin also costs money in ways that are easy to miss. Staff process orders more slowly, product updates get postponed, and a server that struggles with the back office is often one busy sale away from struggling with the front end too.&lt;/p&gt;

&lt;p&gt;This guide is the order I work in in my &lt;a href="https://aqibjavaid.site/services/wordpress-woocommerce-development" rel="noopener noreferrer"&gt;WordPress and WooCommerce development work&lt;/a&gt; when I'm asked to speed up a store: find what is slow, fix the database and background tasks, then look at hosting. It is written for store owners who want to understand what a developer will check, and for developers who want a checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is the WooCommerce admin slow?
&lt;/h2&gt;

&lt;p&gt;The storefront and the admin behave very differently. Visitors mostly get cached pages, so the store can feel quick even when the server is struggling. The admin is never cached. Every click runs PHP and database queries in full, so any weakness shows up there first.&lt;/p&gt;

&lt;p&gt;The usual causes, roughly in the order I find them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Too many or heavy plugins.&lt;/strong&gt; Each plugin can add database queries, remote API calls and scripts to every admin page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A bloated &lt;code&gt;wp_options&lt;/code&gt; table.&lt;/strong&gt; Plugins store data there that loads on every request, including pages you never see.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orders stored in the old posts tables.&lt;/strong&gt; Large stores slow down when orders live in the general posts and post meta tables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A backlog of background tasks.&lt;/strong&gt; WooCommerce relies on the Action Scheduler for emails, webhooks and more. A pile of failed or pending actions makes everything heavier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No object cache and an old PHP version.&lt;/strong&gt; These make every database-heavy screen slower than it needs to be.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Underpowered hosting.&lt;/strong&gt; Shared hosting with limited PHP workers and slow storage can't keep up with a growing catalogue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Guessing at this list wastes time. Measure first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the real bottleneck before changing anything
&lt;/h2&gt;

&lt;p&gt;Take a full backup before you start. Then install the free &lt;a href="https://wordpress.org/plugins/query-monitor/" rel="noopener noreferrer"&gt;Query Monitor plugin&lt;/a&gt; on a staging copy of the site, or on production for a short test if you have no staging.&lt;/p&gt;

&lt;p&gt;Open the slow screen, for example WooCommerce, Orders, and look at three things in Query Monitor:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Slow queries.&lt;/strong&gt; It lists queries by time and tells you which plugin or theme triggered each one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queries by component.&lt;/strong&gt; If one plugin owns most of the query time, you have your first suspect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP API calls.&lt;/strong&gt; Remote requests made while the page loads, for example a plugin checking a licence server, can add seconds by themselves.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Write down the load time and the biggest offenders before you change anything. You will need those numbers to prove each fix helped.&lt;/p&gt;

&lt;p&gt;If you know how to read the MySQL side of this, the same measure-first approach I described in the &lt;a href="https://aqibjavaid.site/blog/laravel-slow-queries-fix" rel="noopener noreferrer"&gt;guide to finding slow MySQL queries in Laravel&lt;/a&gt; applies here: the slowest total time matters more than any single dramatic query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 1: Turn on High-Performance Order Storage
&lt;/h2&gt;

&lt;p&gt;By default, older WooCommerce stores keep orders in WordPress's general posts tables. &lt;a href="https://woocommerce.com/document/high-performance-order-storage/" rel="noopener noreferrer"&gt;High-Performance Order Storage (HPOS)&lt;/a&gt; moves orders into dedicated tables, which makes order screens and queries faster as the number of orders grows.&lt;/p&gt;

&lt;p&gt;You enable it under WooCommerce, Settings, Advanced, Features. Before you do, check that every plugin that touches orders (payments, shipping, invoicing, subscriptions) says it supports HPOS, and run the migration on staging first. WooCommerce can keep both storage locations in sync during a transition, so you can switch back if something breaks. New stores on recent WooCommerce versions use HPOS by default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 2: Clean up &lt;code&gt;wp_options&lt;/code&gt; autoload data
&lt;/h2&gt;

&lt;p&gt;Every row in &lt;code&gt;wp_options&lt;/code&gt; marked as autoload is loaded on every request. Plugins that have been removed often leave large rows behind. To see the biggest ones, run this on a backup or staging database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;option_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;LENGTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;option_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;wp_options&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;autoload&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'yes'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'on'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'auto'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'auto-on'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&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;autoload&lt;/code&gt; values differ between WordPress versions, which is why the query lists several. If the total autoloaded size is several megabytes, or a handful of rows dominate it, you have found a real problem. Remove rows that belong to plugins you've uninstalled, and ask the plugin vendor about the rest. Don't delete rows you can't identify.&lt;/p&gt;

&lt;p&gt;Expired transients also pile up. You can clear them safely with &lt;a href="https://developer.wordpress.org/cli/commands/" rel="noopener noreferrer"&gt;WP-CLI&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp transient delete &lt;span class="nt"&gt;--expired&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fix 3: Clear the Action Scheduler backlog
&lt;/h2&gt;

&lt;p&gt;Go to WooCommerce, Status, Scheduled Actions. If you see thousands of pending or past-due actions, or a long list of failed ones, the scheduler is working against you. Find out what created them: a misconfigured email plugin, a webhook that keeps failing, or a sync job that runs too often.&lt;/p&gt;

&lt;p&gt;Fix the source first. If you only delete the backlog, it will come back. Once the cause is gone, you can clear the old completed and failed actions, and WooCommerce will clean up completed ones on its own schedule.&lt;/p&gt;

&lt;p&gt;Also check that WordPress's own cron isn't doing heavy work during page loads. On a busy store, it's common to disable the page-load trigger and run a real server cron job instead, which makes the timing predictable and removes the work from visitors' requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 4: Cut plugin weight
&lt;/h2&gt;

&lt;p&gt;Deactivate plugins one at a time on staging and reload the slow screen. When a plugin makes a big difference, ask whether you really need it, whether a lighter one does the same job, or whether a few lines of custom code replace it. This is where a developer earns their fee: replacing three heavy plugins with one small piece of code often speeds up the admin more than any server upgrade.&lt;/p&gt;

&lt;p&gt;Watch for plugins that run on every admin page but only matter on one, such as analytics widgets, marketing dashboards and "suggestion" panels. Many have a setting to turn the dashboard widget off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix 5: Add an object cache and update PHP
&lt;/h2&gt;

&lt;p&gt;A persistent object cache such as Redis stores the results of repeated database lookups, so the admin doesn't repeat them on every click. It matters most on stores with many products, many orders or heavy plugins. I wrote about how to set up caching without serving stale data in the &lt;a href="https://aqibjavaid.site/blog/laravel-redis-caching-architecture" rel="noopener noreferrer"&gt;Redis caching architecture article&lt;/a&gt;. The invalidation rules there apply to any application, not just Laravel.&lt;/p&gt;

&lt;p&gt;Check your PHP version at the same time. WordPress publishes the &lt;a href="https://wordpress.org/about/requirements/" rel="noopener noreferrer"&gt;versions it recommends&lt;/a&gt;, and each newer PHP release is generally faster than the one before. Test plugin compatibility on staging, then upgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the hosting is the problem
&lt;/h2&gt;

&lt;p&gt;If the plugins are tidy, the database is clean and the admin is still slow, look at hosting. Signs include a low PHP worker limit, slow disk, a database server shared with many other sites, or no ability to install an object cache.&lt;/p&gt;

&lt;p&gt;When I worked on the &lt;a href="https://aqibjavaid.site/work/henceforward-wordpress-migration-security-optimization" rel="noopener noreferrer"&gt;Henceforward WordPress migration&lt;/a&gt;, the project covered moving the site to Scaleway with Laravel Forge, strengthening security and tuning the production environment for reliability and performance. Moving to a server you control is not always the answer, but it gives you the options (caching, PHP settings, server-level cron) that cheap shared hosting takes away. For a broader look at stabilising a production setup, see the write-up on &lt;a href="https://aqibjavaid.site/blog/stabilizing-aws-production-environment-devops" rel="noopener noreferrer"&gt;fixing an unstable AWS production environment&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to keep patching
&lt;/h2&gt;

&lt;p&gt;Sometimes the honest answer is that the store has outgrown WordPress. If your plugin stack is enormous, you rely on heavy custom logic, or you need real-time inventory and order flows, a custom build may cost less over three years than constant tuning. I compared those trade-offs in &lt;a href="https://aqibjavaid.site/blog/shopify-vs-custom-ecommerce" rel="noopener noreferrer"&gt;Shopify versus custom ecommerce&lt;/a&gt;. For most WooCommerce stores, though, the fixes above are enough, and tuning an existing store is exactly the kind of work I take on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why is my WooCommerce admin slow but my storefront fast?
&lt;/h3&gt;

&lt;p&gt;The storefront is usually served from a page cache. The admin can't be cached, so slow queries, heavy plugins and background tasks only show up there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will a caching plugin fix a slow WooCommerce admin?
&lt;/h3&gt;

&lt;p&gt;Page caching plugins don't help the admin. An object cache can, but it won't fix a bloated options table or a plugin making slow remote calls. Find the cause first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it safe to enable HPOS on a live store?
&lt;/h3&gt;

&lt;p&gt;Test on staging first and confirm that every order-related plugin supports it. Keep a backup, and use the compatibility mode that syncs both storage locations while you check everything works.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does it take to fix a slow WooCommerce admin?
&lt;/h3&gt;

&lt;p&gt;It depends on the cause. A cleanup of options, transients and scheduled actions can be done quickly. Replacing plugins or moving hosts takes longer and needs testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The admin is slow because it can't be cached, so measure it with Query Monitor before you change anything.&lt;/li&gt;
&lt;li&gt;The big wins are HPOS, a clean &lt;code&gt;wp_options&lt;/code&gt; table, a cleared Action Scheduler backlog and fewer heavy plugins.&lt;/li&gt;
&lt;li&gt;Add an object cache and a current PHP version after the basics, not instead of them.&lt;/li&gt;
&lt;li&gt;If hosting limits you, move to a setup you can tune, and know when a store has outgrown WordPress.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your WooCommerce admin is holding your team back and you want a senior developer to find the cause, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a free call&lt;/a&gt; and we can go through it together.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/woocommerce-slow-admin-fix" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>woocommerce</category>
      <category>performance</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>How to Find and Fix Slow MySQL Queries in Laravel</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Wed, 07 Oct 2026 00:02:36 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/how-to-find-and-fix-slow-mysql-queries-in-laravel-3806</link>
      <guid>https://dev.to/aqib_javaid/how-to-find-and-fix-slow-mysql-queries-in-laravel-3806</guid>
      <description>&lt;p&gt;A Laravel page that took 200 milliseconds in development takes three seconds in production, and nobody changed the code. That is the usual story behind a &lt;strong&gt;Laravel slow queries fix&lt;/strong&gt;: the data grew, an index was never added, or one innocent-looking loop started firing hundreds of queries per request.&lt;/p&gt;

&lt;p&gt;The good news is that slow MySQL queries are among the most fixable performance problems. You don't need a rewrite or a bigger server. You need to find the queries that actually hurt, read what MySQL says about them, and apply one of a handful of well-known fixes.&lt;/p&gt;

&lt;p&gt;This guide is the process I follow in my &lt;a href="https://aqibjavaid.site/services/database-optimization-backend-performance" rel="noopener noreferrer"&gt;database optimization and backend performance work&lt;/a&gt; when I'm asked to find and fix slow queries in a Laravel app: measure first, read the query plan, fix the cause, and prove the result. It is written for developers doing the work and for founders who want to know what a performance fix really involves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start by measuring, not guessing
&lt;/h2&gt;

&lt;p&gt;The most common mistake is adding indexes or cache layers before knowing which queries are slow. Two kinds of problem hide behind "the page is slow":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One slow query.&lt;/strong&gt; A single statement scans a big table and takes seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Many fast queries.&lt;/strong&gt; Each takes 2 milliseconds, but there are 400 of them on one page. This is the classic N+1 problem, and no index will fix it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Laravel lets you catch both. For a quick look at one request, log every query with its time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Illuminate\Support\Facades\DB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'bindings'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'ms'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;time&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;For production, you want an alarm instead of a firehose. &lt;code&gt;whenQueryingForLongerThan&lt;/code&gt; runs a callback when the total time spent in queries during a request passes a limit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Connection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Illuminate\Support\Facades\DB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;whenQueryingForLongerThan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Connection&lt;/span&gt; &lt;span class="nv"&gt;$connection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Slow request: over 500ms spent in queries'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fullUrl&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;Put either snippet in a service provider's &lt;code&gt;boot&lt;/code&gt; method. The second one tells you which pages to look at first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn on the MySQL slow query log
&lt;/h2&gt;

&lt;p&gt;Laravel shows you what the application sent. MySQL's own &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/slow-query-log.html" rel="noopener noreferrer"&gt;slow query log&lt;/a&gt; shows what the database really spent time on, including queries from queue workers and scheduled jobs that your request-level tools never see.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;GLOBAL&lt;/span&gt; &lt;span class="n"&gt;slow_query_log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ON'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;GLOBAL&lt;/span&gt; &lt;span class="n"&gt;long_query_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="k"&gt;GLOBAL&lt;/span&gt; &lt;span class="n"&gt;log_queries_not_using_indexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ON'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those settings last until the server restarts, so add them to the MySQL configuration file once you are happy with them. On a busy server, leave &lt;code&gt;log_queries_not_using_indexes&lt;/code&gt; off at first because it can be noisy, and set &lt;code&gt;long_query_time&lt;/code&gt; to something like one second.&lt;/p&gt;

&lt;p&gt;After a day of normal traffic, group the log by query shape. MySQL ships with &lt;code&gt;mysqldumpslow&lt;/code&gt;, and Percona's &lt;code&gt;pt-query-digest&lt;/code&gt; does the same job in more detail. You are looking for the queries with the highest &lt;em&gt;total&lt;/em&gt; time, which is the number of runs multiplied by the average duration. A 100 ms query that runs 50,000 times a day matters more than a 4-second report that runs twice.&lt;/p&gt;

&lt;p&gt;If you already have error tracking and metrics in place, the same signals belong there too. I covered that setup in the &lt;a href="https://aqibjavaid.site/blog/laravel-observability-architecture" rel="noopener noreferrer"&gt;Laravel observability guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read the query plan with EXPLAIN
&lt;/h2&gt;

&lt;p&gt;Once you have a suspect, ask MySQL how it runs the query. Prefix it with &lt;code&gt;EXPLAIN&lt;/code&gt;, or call &lt;code&gt;explain()&lt;/code&gt; on a query builder in a recent Laravel version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="no"&gt;DB&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'orders'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'customer_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4821&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'paid'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;orderByDesc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'created_at'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;explain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reading the &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/explain-output.html" rel="noopener noreferrer"&gt;EXPLAIN output&lt;/a&gt; takes practice, but four columns do most of the work:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Column&lt;/th&gt;
&lt;th&gt;What to look for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ALL&lt;/code&gt; means a full table scan. &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;range&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; mean an index is used.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The index MySQL chose. &lt;code&gt;NULL&lt;/code&gt; means none.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MySQL's estimate of rows it must examine. Compare it with the rows you actually return.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Extra&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Using filesort&lt;/code&gt; and &lt;code&gt;Using temporary&lt;/code&gt; mean extra work to sort or group the result.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If a query returns 50 rows but &lt;code&gt;rows&lt;/code&gt; says 2,400,000, MySQL is reading the whole table to find them. That is the gap you close with an index. On MySQL 8.0.18 and later, &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; goes further and runs the query to show real timings next to the estimates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix the five causes I see most often
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. A missing or wrong index
&lt;/h3&gt;

&lt;p&gt;For the query above, the right index covers the equality filters first and the sort column last:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Schema&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'orders'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Blueprint&lt;/span&gt; &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'customer_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'status'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'created_at'&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;Column order matters. Put columns you compare with &lt;code&gt;=&lt;/code&gt; first, then the column you sort or range-filter on. An index on &lt;code&gt;status&lt;/code&gt; alone is usually useless, because a column with a few distinct values barely narrows the search.&lt;/p&gt;

&lt;p&gt;Don't add an index for every query. Each index slows down writes and uses disk. Add the ones your slowest, most frequent queries need, then re-run &lt;code&gt;EXPLAIN&lt;/code&gt; to confirm MySQL uses them.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The N+1 problem
&lt;/h3&gt;

&lt;p&gt;This loop looks harmless:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$orders&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;customer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// one extra query per order&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is 51 queries for a 50-row page. Load the relationship up front, as the &lt;a href="https://laravel.com/docs/eloquent-relationships#eager-loading" rel="noopener noreferrer"&gt;Laravel eager loading docs&lt;/a&gt; describe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'customer:id,name'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To stop N+1 from coming back, make Laravel complain about it in development. In a service provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;preventLazyLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isProduction&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now a lazy-loaded relationship throws an exception while you are building the feature, instead of costing you a slow page in production. Many teams find this one line pays for itself within a week.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Selecting far more than you need
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;SELECT *&lt;/code&gt; on a wide table drags large text and JSON columns across the wire for every row, even when the page shows two fields. Select only the columns you use, and use &lt;code&gt;withCount()&lt;/code&gt; or &lt;code&gt;withSum()&lt;/code&gt; instead of loading a whole relationship to count it.&lt;/p&gt;

&lt;p&gt;For jobs that walk a big table, use &lt;code&gt;chunkById()&lt;/code&gt; instead of &lt;code&gt;get()&lt;/code&gt;. It keeps memory flat and avoids the skipped or repeated rows that plain &lt;code&gt;chunk()&lt;/code&gt; can produce when you update the rows you're reading.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Queries that can't use an index
&lt;/h3&gt;

&lt;p&gt;Some queries ignore a perfectly good index:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrapping the column in a function: &lt;code&gt;WHERE DATE(created_at) = '2026-10-01'&lt;/code&gt;. Use a range instead: &lt;code&gt;created_at &amp;gt;= '2026-10-01' AND created_at &amp;lt; '2026-10-02'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A leading wildcard: &lt;code&gt;WHERE name LIKE '%smith'&lt;/code&gt;. An index can't help here. Real search needs a different tool, which I wrote about in the &lt;a href="https://aqibjavaid.site/blog/laravel-full-text-search-architecture" rel="noopener noreferrer"&gt;full-text search architecture article&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Comparing a number column to a string, which can force MySQL to convert every row.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Deep pagination and expensive counts
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;paginate()&lt;/code&gt; runs a &lt;code&gt;COUNT(*)&lt;/code&gt; query on every page, and &lt;code&gt;OFFSET 200000&lt;/code&gt; makes MySQL read and throw away 200,000 rows before returning yours. For large tables, switch to &lt;code&gt;simplePaginate()&lt;/code&gt; when you don't need page numbers, or &lt;code&gt;cursorPaginate()&lt;/code&gt; for infinite scroll and APIs. Both avoid the deep offset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prove it worked
&lt;/h2&gt;

&lt;p&gt;Never trust a fix you haven't measured. For every change:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Record the query time and the &lt;code&gt;EXPLAIN&lt;/code&gt; output before.&lt;/li&gt;
&lt;li&gt;Apply one change.&lt;/li&gt;
&lt;li&gt;Re-run &lt;code&gt;EXPLAIN&lt;/code&gt; and time the query with production-sized data. A fast query on a 500-row dev database proves nothing.&lt;/li&gt;
&lt;li&gt;Watch the slow query log for a day to confirm the real traffic improved.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Run index migrations carefully on a big table. Adding an index to millions of rows can lock or slow the table while it builds, so schedule it for a quiet period, and test the migration on a copy of production data first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I look at when the database is the bottleneck
&lt;/h2&gt;

&lt;p&gt;On client work, performance problems rarely come from one place. When I audited the &lt;a href="https://aqibjavaid.site/work/jamaica-freight-forwarding-core-php-audit" rel="noopener noreferrer"&gt;freight forwarding platform&lt;/a&gt;, reviewing the database structure was part of understanding the system before any new development. That is the same habit this guide describes: read how the data is organised before you tune individual queries.&lt;/p&gt;

&lt;p&gt;On data-heavy products like &lt;a href="https://aqibjavaid.site/work/violerts-nyc-proptech-compliance-saas" rel="noopener noreferrer"&gt;Violerts&lt;/a&gt;, which pulls property data from several city agencies into one platform, the shape of the queries matters as much as the infrastructure around them. If your app is slow and you aren't sure where the time goes, that service starts with exactly this measurement step.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to add a cache
&lt;/h2&gt;

&lt;p&gt;Caching is the tempting shortcut: wrap the slow query in &lt;code&gt;Cache::remember()&lt;/code&gt; and move on. It works, but it hides the problem, and you inherit a new one, which is knowing when the cached data is stale. Fix the query first. Add caching only for queries that are already well-indexed and still too heavy to run on every request. The &lt;a href="https://aqibjavaid.site/blog/laravel-redis-caching-architecture" rel="noopener noreferrer"&gt;Redis caching architecture guide&lt;/a&gt; covers how to do that without serving wrong data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I find slow queries in Laravel?
&lt;/h3&gt;

&lt;p&gt;Log query times with &lt;code&gt;DB::listen&lt;/code&gt;, set an alarm with &lt;code&gt;DB::whenQueryingForLongerThan&lt;/code&gt;, and turn on MySQL's slow query log. Start with the queries that have the highest total time, not just the slowest single run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will adding an index always make a query faster?
&lt;/h3&gt;

&lt;p&gt;No. An index only helps if MySQL chooses it, and it adds cost to every write. Check with &lt;code&gt;EXPLAIN&lt;/code&gt; before and after, and remove indexes that nothing uses.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is Telescope good enough for finding slow queries?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://laravel.com/docs/telescope" rel="noopener noreferrer"&gt;Laravel Telescope&lt;/a&gt; is useful in development and staging because it lists the queries for each request and flags slow ones. In production it adds overhead, so most teams rely on the slow query log and an observability tool there.&lt;/p&gt;

&lt;h3&gt;
  
  
  When is the problem not the queries?
&lt;/h3&gt;

&lt;p&gt;If query time is small and the page is still slow, look at external API calls, large responses, missing queue offloading or a saturated server. Measure before blaming the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Measure first: find the queries with the highest total time, not the ones you suspect.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;EXPLAIN&lt;/code&gt; to see whether MySQL scans the whole table or uses an index.&lt;/li&gt;
&lt;li&gt;Most Laravel slowdowns come from five causes: missing indexes, N+1 loading, selecting too much, queries that can't use an index, and deep pagination.&lt;/li&gt;
&lt;li&gt;Turn on &lt;code&gt;preventLazyLoading&lt;/code&gt; in development to catch N+1 early.&lt;/li&gt;
&lt;li&gt;Prove every fix with before and after numbers on production-sized data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your Laravel app is getting slower as it grows and you want a senior engineer to find the cause, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a free call&lt;/a&gt; and we can look at it together.&lt;/p&gt;




&lt;p&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/laravel-slow-queries-fix" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>mysql</category>
      <category>php</category>
      <category>performance</category>
    </item>
    <item>
      <title>Shopify vs Custom Ecommerce: When Custom Is Worth It</title>
      <dc:creator>Aqib Javaid</dc:creator>
      <pubDate>Mon, 05 Oct 2026 05:46:52 +0000</pubDate>
      <link>https://dev.to/aqib_javaid/shopify-vs-custom-ecommerce-when-custom-is-worth-it-3lee</link>
      <guid>https://dev.to/aqib_javaid/shopify-vs-custom-ecommerce-when-custom-is-worth-it-3lee</guid>
      <description>&lt;p&gt;If you're launching an online store, the &lt;strong&gt;Shopify vs custom ecommerce&lt;/strong&gt; question usually comes up in the first week. Shopify is quick and proven. A custom build can do exactly what you want. The wrong choice either burns money on engineering you didn't need or traps a growing business inside a platform it has outgrown.&lt;/p&gt;

&lt;p&gt;I build both kinds of stores and integrations, so I don't need either answer to be right. In this guide I'll explain what you're really comparing, when Shopify is the smarter choice, the specific situations where custom is worth it, how to compare the real cost over three years, and a short checklist to decide.&lt;/p&gt;

&lt;p&gt;If you already know you need something Shopify can't do, that's the core of my &lt;a href="https://aqibjavaid.site/services/ecommerce-development-integrations" rel="noopener noreferrer"&gt;ecommerce development and integrations work&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are you actually comparing?
&lt;/h2&gt;

&lt;p&gt;The two options are different in kind.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shopify&lt;/strong&gt; is a hosted store platform. Hosting, checkout, payments, product management, themes and an app store come in one package, and you pay a monthly plan plus fees. You configure it rather than build it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom ecommerce&lt;/strong&gt; means a store built in code on a framework such as Laravel or Next.js, often with Stripe for payments and your own admin. You own the code, you pay to build it, and you pay to host and maintain it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also a middle path: keep Shopify as the engine for products, checkout and orders, and build custom pieces around it, such as a custom storefront, integrations with your ERP or shipping systems, or special pricing logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shopify vs custom ecommerce: side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;Shopify&lt;/th&gt;
&lt;th&gt;Custom ecommerce&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time to launch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Days to weeks&lt;/td&gt;
&lt;td&gt;Months&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Upfront cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ongoing cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monthly plan, app subscriptions and transaction fees&lt;/td&gt;
&lt;td&gt;Hosting, maintenance and developer time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Checkout control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited, and more flexible only on higher plans&lt;/td&gt;
&lt;td&gt;Complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Custom pricing and workflows&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Possible through apps and workarounds&lt;/td&gt;
&lt;td&gt;Built exactly to fit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integrations (ERP, shipping, inventory)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Apps and APIs, within platform limits&lt;/td&gt;
&lt;td&gt;Any system with an API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security and PCI burden&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Handled by Shopify&lt;/td&gt;
&lt;td&gt;Yours, though a provider like Stripe carries card data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;You own&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Your data and content, not the platform&lt;/td&gt;
&lt;td&gt;The code, the data and the platform&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither column wins outright. Each row trades speed and convenience against control.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Shopify is the better choice
&lt;/h2&gt;

&lt;p&gt;For most new stores, Shopify is the right place to start. Choose it when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you sell standard products with standard variants, shipping and discounts;&lt;/li&gt;
&lt;li&gt;you want to launch quickly and test whether the product sells;&lt;/li&gt;
&lt;li&gt;you don't have a developer or a big budget for custom work;&lt;/li&gt;
&lt;li&gt;your edge is your product and marketing, not how checkout works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A store that works on Shopify costs far less to launch and maintain than a custom one, and a lot of apps already cover common needs. Don't build custom to avoid a monthly fee when your real risk is not selling anything yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  When custom ecommerce is worth it
&lt;/h2&gt;

&lt;p&gt;Custom starts to pay off when the platform's limits cost you money or block your model. The situations I see most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pricing or products that don't fit a standard catalogue:&lt;/strong&gt; B2B price lists per customer, quotes, configurable products, rental or booking-style items.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checkout or order flow that must be unique:&lt;/strong&gt; special approvals, multi-step orders, split shipments, custom payment flows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep integration with other systems:&lt;/strong&gt; an ERP or warehouse system as the source of truth, real-time shipping rates from carriers, or live stock from suppliers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A marketplace:&lt;/strong&gt; several vendors, payouts, messaging, and rules between buyers and sellers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fees that become material at scale:&lt;/strong&gt; when plan fees, app subscriptions and gateway surcharges add up to a meaningful share of revenue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ownership and flexibility:&lt;/strong&gt; you want the freedom to change anything without asking a platform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check Shopify's current &lt;a href="https://www.shopify.com/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt; and its notes on &lt;a href="https://help.shopify.com/en/manual/checkout-settings/checkout-customization" rel="noopener noreferrer"&gt;checkout customization&lt;/a&gt; before deciding, since plans, fees and what each plan allows change over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The middle path: Shopify plus custom pieces
&lt;/h2&gt;

&lt;p&gt;You don't have to pick one extreme. Many stores keep Shopify for what it does well and add custom pieces for what it doesn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a custom storefront for speed and design freedom, while Shopify handles products and checkout;&lt;/li&gt;
&lt;li&gt;a custom app or service that syncs orders and stock with your ERP or warehouse;&lt;/li&gt;
&lt;li&gt;custom pricing, quoting or shipping logic that runs beside the store.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is often the cheapest way to remove the one painful limit without rebuilding everything. If you're weighing a custom front end, my &lt;a href="https://aqibjavaid.site/blog/nextjs-vs-wordpress-for-business" rel="noopener noreferrer"&gt;comparison of Next.js and WordPress for business sites&lt;/a&gt; covers the same trade-offs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to compare the real cost over three years
&lt;/h2&gt;

&lt;p&gt;Comparing the monthly bill with the build price is misleading. Compare total cost over three years instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Shopify 3-year cost =
    plan fees x 36
  + apps x 36
  + transaction and gateway surcharges (a share of revenue)
  + theme and workaround development

Custom 3-year cost =
    build cost
  + hosting x 36
  + maintenance (about 15-20% of build cost per year)
  + payment processor fees
  + developer time for new features
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two points matter most. First, payment processing fees apply on both sides, so compare the extra costs: Shopify's plan, app and surcharge costs against your custom build and upkeep. Second, as revenue grows, percentage-based costs grow with it, while a custom platform's costs stay mostly fixed.&lt;/p&gt;

&lt;p&gt;As a rough ballpark, and not a quote, I'd estimate a custom store with a standard catalogue, Stripe payments and an admin area at 300 to 800 hours of work. One with B2B pricing, custom checkout rules and ERP integration is more like 800 to 2,000 hours or more. At a wide $25 to $100 an hour, that's roughly $7,500 to $80,000 for the first and $20,000 to $200,000 or more for the second. For general budgeting logic, see &lt;a href="https://aqibjavaid.site/blog/custom-web-app-mvp-cost-2026" rel="noopener noreferrer"&gt;what it costs to build an MVP&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples from real projects
&lt;/h2&gt;

&lt;p&gt;Three projects show where custom logic earned its place.&lt;/p&gt;

&lt;p&gt;On the &lt;a href="https://aqibjavaid.site/work/expreco-logistics-quote-system" rel="noopener noreferrer"&gt;EXPRECO logistics quote system&lt;/a&gt;, I built a quote platform in Laravel and Livewire that integrates the UPS and FedEx APIs to calculate real-time shipping rates from package details and destinations. Live carrier rates tied to a custom workflow are the kind of requirement that pushes a store towards custom or custom-around-a-platform.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://aqibjavaid.site/work/stripe-lead-billing-gohighlevel" rel="noopener noreferrer"&gt;Stripe lead billing with GoHighLevel&lt;/a&gt;, I built a Stripe workflow that saves payment methods without an immediate charge and charges them automatically when qualified leads are delivered, with webhook-driven status sync. A payment model like that doesn't fit a standard checkout.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://aqibjavaid.site/work/planuojam-marketplace-case-study" rel="noopener noreferrer"&gt;Planuojam&lt;/a&gt; is a headless marketplace in five languages with real-time messaging and Stripe payments. Marketplaces are a classic case where a standard store platform stops fitting. For the integration side of any of these, see my notes on &lt;a href="https://aqibjavaid.site/blog/third-party-api-integration-best-practices" rel="noopener noreferrer"&gt;third-party API integration best practices&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A simple decision checklist
&lt;/h2&gt;

&lt;p&gt;Answer these honestly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Can you describe your catalogue and checkout with standard features,&lt;/strong&gt; or do you need custom pricing and order rules?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Do you need to connect an ERP, warehouse or carrier system in real time?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is this a marketplace&lt;/strong&gt; with several vendors or special payment flows?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Have you proven demand yet,&lt;/strong&gt; or are you still testing the idea?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What does the platform cost you at your expected revenue,&lt;/strong&gt; including plans, apps and fees?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Mostly "standard" and "still testing" points to Shopify. Custom pricing, deep integrations and a marketplace model point to custom, or to Shopify with custom pieces. If it's close, start on Shopify and revisit once a specific limit is costing you real money.&lt;/p&gt;

&lt;h2&gt;
  
  
  When not to go custom
&lt;/h2&gt;

&lt;p&gt;Custom is the wrong call if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you haven't validated that people will buy;&lt;/li&gt;
&lt;li&gt;your needs fit standard features and a few apps;&lt;/li&gt;
&lt;li&gt;you can't fund both the build and ongoing maintenance;&lt;/li&gt;
&lt;li&gt;nobody on your side can own the product after launch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is custom ecommerce cheaper than Shopify?
&lt;/h3&gt;

&lt;p&gt;Not at the start. A custom build costs far more upfront. Over several years it can be cheaper for large stores where percentage-based fees and app subscriptions add up, or where Shopify can't support your model at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does a custom ecommerce site take to build?
&lt;/h3&gt;

&lt;p&gt;A simple custom store takes a few months with an experienced developer. Stores with custom pricing, integrations or marketplace features take longer. Launching a smaller first version shortens the timeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I move from Shopify to custom later?
&lt;/h3&gt;

&lt;p&gt;Yes. Many businesses start on Shopify and move, or add custom pieces, once a specific limit is costing them. Plan the move so you keep your URLs, product data and customer accounts.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about payments and card security on a custom store?
&lt;/h3&gt;

&lt;p&gt;Use a provider such as &lt;a href="https://stripe.com/docs" rel="noopener noreferrer"&gt;Stripe&lt;/a&gt; so card data never touches your servers. That keeps your compliance burden far smaller than handling cards yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Shopify is a hosted platform you configure. Custom ecommerce is a store you build and own.&lt;/li&gt;
&lt;li&gt;Start with Shopify for standard products and fast testing.&lt;/li&gt;
&lt;li&gt;Go custom, or custom around Shopify, for unusual pricing, checkout, integrations or marketplaces.&lt;/li&gt;
&lt;li&gt;Compare total cost over three years, not the monthly bill against the build price.&lt;/li&gt;
&lt;li&gt;Budget 15-20% of a custom build's cost per year for upkeep.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're deciding between Shopify and a custom build, &lt;a href="https://aqibjavaid.site/book" rel="noopener noreferrer"&gt;book a call&lt;/a&gt; and I'll help you pick the option that fits your store and your budget.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aqibjavaid.site/blog/shopify-vs-custom-ecommerce" rel="noopener noreferrer"&gt;aqibjavaid.site&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ecommerce</category>
      <category>shopify</category>
      <category>webdev</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
