<?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: DeployHQ</title>
    <description>The latest articles on DEV Community by DeployHQ (@deployhq).</description>
    <link>https://dev.to/deployhq</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1687924%2F3c97db5e-145a-4aae-adbe-b57f149a6ec3.png</url>
      <title>DEV Community: DeployHQ</title>
      <link>https://dev.to/deployhq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/deployhq"/>
    <language>en</language>
    <item>
      <title>Deployment Agents Compared: DeployHQ vs Buddy vs Octopus Deploy</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Wed, 04 Mar 2026 13:29:00 +0000</pubDate>
      <link>https://dev.to/deployhq/deployment-agents-compared-deployhq-vs-buddy-vs-octopus-deploy-el1</link>
      <guid>https://dev.to/deployhq/deployment-agents-compared-deployhq-vs-buddy-vs-octopus-deploy-el1</guid>
      <description>&lt;p&gt;When your servers sit behind a firewall, most deployment tools cannot reach them. The standard fix is a deployment agent — a lightweight process that runs inside your private network, connects outbound to the deployment platform, and routes deployment traffic through that tunnel.&lt;/p&gt;

&lt;p&gt;Several tools offer this pattern, but the implementations differ significantly. This guide compares three deployment agents side by side: the &lt;a href="https://www.deployhq.com/features/deploy-behind-firewalls" rel="noopener noreferrer"&gt;DeployHQ Agent&lt;/a&gt;, the &lt;a href="https://buddy.works/docs/tunnels-agents/proxy-agent" rel="noopener noreferrer"&gt;Buddy Proxy Agent&lt;/a&gt;, and the &lt;a href="https://octopus.com/docs/infrastructure/deployment-targets/tentacle" rel="noopener noreferrer"&gt;Octopus&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; Polling Tentacle — covering architecture, setup, protocols, security, pricing, and the scenarios where each one fits best.&lt;/p&gt;

&lt;p&gt;If you are not sure whether you need a deployment agent in the first place, read our guide on &lt;a href="https://www.deployhq.com/blog/deploying-behind-firewalls-made-easy-introducing-the-deployhq-agent" rel="noopener noreferrer"&gt;how to deploy to servers behind a firewall&lt;/a&gt;, which compares agents against SSH tunnels, VPNs, bastion hosts, and self-hosted CI/CD runners.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Deployment Agents Work
&lt;/h2&gt;

&lt;p&gt;All three tools solve the same fundamental problem: your deployment platform lives in the cloud, your server lives behind a firewall, and the firewall blocks inbound connections.&lt;/p&gt;

&lt;p&gt;The solution is to flip the direction. Instead of the platform connecting inbound to your server, an agent on your server connects outbound to the platform. Since firewalls typically allow outbound traffic, the connection goes through. The platform then sends deployment instructions back through this established tunnel.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Agent] --&amp;gt;|Outbound connection| B[Platform]
    B --&amp;gt;|Deploy traffic| A
    A --&amp;gt;|Local network| C[Server]
    style A fill:#1abc9c,color:#fff

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

&lt;/div&gt;



&lt;p&gt;Where the three tools diverge is in the tunnel protocol, the agent-to-server relationship, and the scope of what the platform does beyond deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DeployHQ Agent: One Tunnel, Any Protocol
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; Agent establishes a persistent outbound TLS connection to &lt;code&gt;agent.deployhq.com&lt;/code&gt; on TCP port 7777. Once the tunnel is up, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; routes deployment traffic through it using whatever protocol the deployment target requires — FTP, SFTP, SSH, or S3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[DeployHQAgent] --&amp;gt;|TLS port 7777| B[DeployHQ]
    B --&amp;gt;|FTP/SFTP/SSH/S3| A
    A --&amp;gt;|Any protocol| C[Server1]
    A --&amp;gt;|Any protocol| D[Server2]
    A --&amp;gt;|Any protocol| E[Server3]
    style A fill:#1abc9c,color:#fff

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

&lt;/div&gt;



&lt;p&gt;The agent acts as a network proxy. One agent instance can deploy to multiple internal servers — you control which ones via an access file (&lt;code&gt;~/.deploy/agent.access&lt;/code&gt;) that accepts individual IPs, hostnames, or CIDR ranges like &lt;code&gt;192.168.1.0/24&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Key technical details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tunnel protocol&lt;/strong&gt; : TLS over TCP (port 7777 outbound)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment protocols&lt;/strong&gt; : FTP, SFTP, SSH, S3 — protocol-agnostic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-to-server ratio&lt;/strong&gt; : One agent, many servers (proxy model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt; : Ruby 2.2+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platforms&lt;/strong&gt; : Linux, macOS, Windows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; : Yes — &lt;a href="https://github.com/deployhq/deploy-agent" rel="noopener noreferrer"&gt;deployhq/deploy-agent on GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnection&lt;/strong&gt; : Automatic — runs as a background service&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Buddy Proxy Agent: SSH Tunnels Under the Hood
&lt;/h3&gt;

&lt;p&gt;The Buddy Proxy Agent establishes an outbound SSH connection to Buddy's infrastructure and keeps a reverse SSH tunnel alive. Buddy routes deployment traffic back through this tunnel to reach servers inside the private network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[BuddyAgent] --&amp;gt;|SSH tunnel| B[Buddy]
    B --&amp;gt;|SSH/SFTP only| A
    A --&amp;gt;|SSH/SFTP| C[Server1]
    A --&amp;gt;|SSH/SFTP| D[Server2]
    style A fill:#9b59b6,color:#fff

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

&lt;/div&gt;



&lt;p&gt;Like DeployHQ's agent, Buddy's agent acts as a proxy — one agent can route traffic to multiple internal servers. You configure target servers as SFTP or SSH targets within Buddy, selecting the agent as the proxy during setup.&lt;/p&gt;

&lt;p&gt;Key technical details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tunnel protocol&lt;/strong&gt; : SSH (reverse tunnel)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment protocols&lt;/strong&gt; : SSH and SFTP only — no FTP or S3 support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-to-server ratio&lt;/strong&gt; : One agent, many servers (proxy model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt; : Docker-based agent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platforms&lt;/strong&gt; : Linux (Docker required)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; : No&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnection&lt;/strong&gt; : Automatic while agent container is running&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Octopus Deploy Polling Tentacle: Agent Per Machine
&lt;/h3&gt;

&lt;p&gt;The Octopus Polling Tentacle takes a fundamentally different approach. Instead of one proxy for the network, you install a Tentacle on every deployment target. Each Tentacle independently polls the Octopus Server over HTTPS (port 443 or 10943) asking for work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Tentacle1] --&amp;gt;|HTTPS poll| B[OctopusServer]
    C[Tentacle2] --&amp;gt;|HTTPS poll| B
    D[Tentacle3] --&amp;gt;|HTTPS poll| B
    style A fill:#e67e22,color:#fff
    style C fill:#e67e22,color:#fff
    style D fill:#e67e22,color:#fff

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

&lt;/div&gt;



&lt;p&gt;When the Octopus Server has a deployment task for a target, it responds to the next poll with the deployment package and instructions. The Tentacle executes the deployment steps locally — no traffic proxying involved.&lt;/p&gt;

&lt;p&gt;Key technical details:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tunnel protocol&lt;/strong&gt; : HTTPS polling (port 443 or 10943 outbound), WebSocket option available&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment protocols&lt;/strong&gt; : Custom Octopus protocol (Halibut) — packages are pushed to the Tentacle, which executes deployment steps locally&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent-to-server ratio&lt;/strong&gt; : One Tentacle per server (per-machine model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Runtime&lt;/strong&gt; : .NET&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platforms&lt;/strong&gt; : Windows, Linux, Docker containers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source&lt;/strong&gt; : Tentacle is open source; Octopus Server is not&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt; : Certificate-based mutual TLS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reconnection&lt;/strong&gt; : Automatic polling on configurable interval&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Side-by-Side Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DeployHQ Agent&lt;/th&gt;
&lt;th&gt;Buddy Proxy Agent&lt;/th&gt;
&lt;th&gt;Octopus Polling Tentacle&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Proxy (one agent, many servers)&lt;/td&gt;
&lt;td&gt;Proxy (one agent, many servers)&lt;/td&gt;
&lt;td&gt;Per-machine (one Tentacle per server)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tunnel protocol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TLS (TCP 7777)&lt;/td&gt;
&lt;td&gt;SSH (reverse tunnel)&lt;/td&gt;
&lt;td&gt;HTTPS polling (443/10943)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment protocols&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FTP, SFTP, SSH, S3&lt;/td&gt;
&lt;td&gt;SSH, SFTP only&lt;/td&gt;
&lt;td&gt;Custom (Halibut)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-server access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes — CIDR-based ACL&lt;/td&gt;
&lt;td&gt;Yes — configure per target&lt;/td&gt;
&lt;td&gt;N/A — one per machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agents to manage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1 per network&lt;/td&gt;
&lt;td&gt;1 per network&lt;/td&gt;
&lt;td&gt;1 per server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime requirement&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ruby 2.2+&lt;/td&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;.NET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platforms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Linux, macOS, Windows&lt;/td&gt;
&lt;td&gt;Linux (Docker)&lt;/td&gt;
&lt;td&gt;Windows, Linux, Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Tentacle only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API token&lt;/td&gt;
&lt;td&gt;SSH key&lt;/td&gt;
&lt;td&gt;Mutual TLS certificates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inbound ports needed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WebSocket support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (Windows)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~5 minutes&lt;/td&gt;
&lt;td&gt;~10 minutes&lt;/td&gt;
&lt;td&gt;~15 minutes per server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Proxy Model vs the Per-Machine Model
&lt;/h2&gt;

&lt;p&gt;This is the most important architectural difference and it affects everything from setup time to security posture.&lt;/p&gt;

&lt;h3&gt;
  
  
  Proxy Model (DeployHQ, Buddy)
&lt;/h3&gt;

&lt;p&gt;One agent sits at the edge of your private network and proxies deployment traffic to internal servers. You install and manage one process. Adding a new server means updating a config file (DeployHQ) or adding a target in the UI (Buddy) — no software installed on the target server itself.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Minimal infrastructure overhead — one process to monitor and maintain&lt;/li&gt;
&lt;li&gt;Adding or removing servers does not require installing or uninstalling agents&lt;/li&gt;
&lt;li&gt;The agent machine is the only one that needs outbound internet access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single point of failure — if the agent goes down, all deployments to that network stop&lt;/li&gt;
&lt;li&gt;The agent machine has network access to every whitelisted server, which means compromising it could allow lateral movement&lt;/li&gt;
&lt;li&gt;Deployment protocols are limited to what the platform supports through the tunnel&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Per-Machine Model (Octopus)
&lt;/h3&gt;

&lt;p&gt;Every deployment target runs its own agent. Each one independently connects to the Octopus Server and executes deployments locally.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;No single point of failure — one Tentacle going down only affects that server&lt;/li&gt;
&lt;li&gt;Each Tentacle can have independent security policies, update schedules, and permissions&lt;/li&gt;
&lt;li&gt;Deployments execute locally on the target, so no file transfer protocol limitations&lt;/li&gt;
&lt;li&gt;Better fit for environments where different teams own different servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More infrastructure to manage — N servers means N agents to install, monitor, patch, and update&lt;/li&gt;
&lt;li&gt;Every target server needs outbound internet access (or access to a shared Octopus Server)&lt;/li&gt;
&lt;li&gt;Adding a new deployment target means installing and configuring a new Tentacle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Which Model Fits?
&lt;/h3&gt;

&lt;p&gt;The proxy model is simpler for small to medium deployments where one team manages a cluster of servers behind a single firewall. The per-machine model makes more sense in enterprise environments with dozens of servers, multiple teams, and compliance requirements that demand per-target audit trails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protocol Support
&lt;/h2&gt;

&lt;p&gt;This is where DeployHQ's agent stands out.&lt;/p&gt;

&lt;p&gt;Many legacy applications and hosting environments rely on FTP or SFTP for deployments — shared hosting with cPanel, older WordPress setups, and environments where SSH access is not available. Buddy's agent only supports SSH and SFTP, which means you cannot deploy to FTP-only servers through the proxy.&lt;/p&gt;

&lt;p&gt;DeployHQ's agent proxies at the network level, supporting FTP, SFTP, SSH, and S3 through the same tunnel. This makes it the only option for teams that need to deploy to a mix of modern and legacy infrastructure behind the same firewall.&lt;/p&gt;

&lt;p&gt;Octopus takes a different approach entirely — the Tentacle does not proxy protocols. Instead, Octopus sends deployment packages to the Tentacle, which executes deployment steps locally. This means protocol support is not a factor because files never transfer through a tunnel in the traditional sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Comparison
&lt;/h2&gt;

&lt;p&gt;All three tools avoid inbound firewall rules, but their security models differ:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeployHQ Agent&lt;/strong&gt; : Outbound TLS on a single port (7777). The access control file explicitly whitelists which internal servers the agent can reach, limiting blast radius if the agent machine is compromised. The agent source code is fully auditable on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Buddy Proxy Agent&lt;/strong&gt; : Outbound SSH tunnel. SSH provides strong encryption, but the agent is not open source, so you cannot audit what runs inside the container. Access control is managed through Buddy's UI rather than a local config file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Octopus Polling Tentacle&lt;/strong&gt; : Outbound HTTPS with certificate-based mutual TLS authentication. This is the strongest authentication model of the three — both the server and the Tentacle verify each other's identity via certificates. However, each Tentacle has full local access to its host machine, and the .NET runtime has a larger attack surface than a Ruby gem or Docker container.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Security Factor&lt;/th&gt;
&lt;th&gt;DeployHQ&lt;/th&gt;
&lt;th&gt;Buddy&lt;/th&gt;
&lt;th&gt;Octopus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encryption&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TLS&lt;/td&gt;
&lt;td&gt;SSH&lt;/td&gt;
&lt;td&gt;HTTPS/TLS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Authentication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API token&lt;/td&gt;
&lt;td&gt;SSH key&lt;/td&gt;
&lt;td&gt;Mutual TLS certificates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access control&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Local file (IP/CIDR)&lt;/td&gt;
&lt;td&gt;Platform UI&lt;/td&gt;
&lt;td&gt;Per-Tentacle certificates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open source agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Tentacle only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attack surface&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ruby gem&lt;/td&gt;
&lt;td&gt;Docker container&lt;/td&gt;
&lt;td&gt;.NET runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Blast radius if compromised&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All whitelisted servers&lt;/td&gt;
&lt;td&gt;All configured targets&lt;/td&gt;
&lt;td&gt;Single server only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;Deployment agents are typically bundled with the platform, but the platforms themselves have very different pricing models.&lt;/p&gt;

&lt;h3&gt;
  
  
  DeployHQ
&lt;/h3&gt;

&lt;p&gt;Pricing is based on projects and servers. The Agent is included on all plans, including the free tier.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Projects&lt;/th&gt;
&lt;th&gt;Servers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;€0/mo&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Solo&lt;/td&gt;
&lt;td&gt;€9/mo&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;15+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;€19/mo&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;50+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business&lt;/td&gt;
&lt;td&gt;€39/mo&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;100+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise&lt;/td&gt;
&lt;td&gt;€99/mo&lt;/td&gt;
&lt;td&gt;50+&lt;/td&gt;
&lt;td&gt;250+&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; is a dedicated deployment tool — it does one thing (deploy code from Git) and the pricing reflects that simplicity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Buddy
&lt;/h3&gt;

&lt;p&gt;Pricing is based on seats and compute minutes. Agents are included, but Buddy is a full CI/CD platform, so you are paying for build pipelines, testing, and deployment together.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Seats&lt;/th&gt;
&lt;th&gt;Key Limits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;€0/mo&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;300 GB-minutes, 1 concurrent pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;€29/mo&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;3,000 GB-minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hyper&lt;/td&gt;
&lt;td&gt;€99/mo&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;6,000 GB-minutes, SSO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Additional seats cost €9-29/month depending on the plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Octopus Deploy
&lt;/h3&gt;

&lt;p&gt;Pricing is based on projects, with add-ons for machines and tenants. Octopus is an enterprise release management platform — significantly more expensive than the other two.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Projects&lt;/th&gt;
&lt;th&gt;Machines&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$0/yr&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Professional (Cloud)&lt;/td&gt;
&lt;td&gt;$4,330/yr&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;10 (add-ons: $770/yr per 10)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise (Cloud)&lt;/td&gt;
&lt;td&gt;$24,600/yr&lt;/td&gt;
&lt;td&gt;100+&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each Polling Tentacle counts as a machine. Since you need one per server, costs scale linearly with your infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost for a Typical Setup
&lt;/h3&gt;

&lt;p&gt;For a team deploying to 10 servers behind a firewall:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DeployHQ&lt;/th&gt;
&lt;th&gt;Buddy&lt;/th&gt;
&lt;th&gt;Octopus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monthly cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;€19 (Pro)&lt;/td&gt;
&lt;td&gt;€29+ (Pro)&lt;/td&gt;
&lt;td&gt;~$361 (Professional)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agents to install&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Includes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deployment only&lt;/td&gt;
&lt;td&gt;Full CI/CD&lt;/td&gt;
&lt;td&gt;Release management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When to Use Each Tool
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Choose DeployHQ Agent When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need to deploy to servers using FTP, SFTP, SSH, or S3 — especially mixed-protocol environments&lt;/li&gt;
&lt;li&gt;You want a single agent proxying to multiple servers with minimal infrastructure overhead&lt;/li&gt;
&lt;li&gt;You prefer an open source agent you can audit&lt;/li&gt;
&lt;li&gt;You use a separate CI/CD tool (GitHub Actions, GitLab CI, Jenkins) and need a dedicated deployment step&lt;/li&gt;
&lt;li&gt;Budget matters — it is the most affordable option for deployment-only use cases&lt;/li&gt;
&lt;li&gt;You deploy to &lt;a href="https://www.deployhq.com/blog/how-to-deploy-to-your-server-using-ssh-sftp-and-git-with-deployhq" rel="noopener noreferrer"&gt;shared hosting, cPanel, or legacy infrastructure&lt;/a&gt; alongside modern servers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose Buddy Proxy Agent When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You want CI/CD and deployment in one platform&lt;/li&gt;
&lt;li&gt;All your servers support SSH or SFTP (no FTP-only targets)&lt;/li&gt;
&lt;li&gt;You need build pipelines running alongside deployment&lt;/li&gt;
&lt;li&gt;Your team is already using Buddy for other CI/CD workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose Octopus Deploy Polling Tentacle When:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need per-server agent isolation for compliance or multi-team environments&lt;/li&gt;
&lt;li&gt;You are deploying .NET applications on Windows infrastructure&lt;/li&gt;
&lt;li&gt;You need enterprise release management features (approvals, tenanted deployments, runbooks)&lt;/li&gt;
&lt;li&gt;You have the budget and team to manage Tentacles across many servers&lt;/li&gt;
&lt;li&gt;Auditing requirements mandate per-target deployment logs and certificate-based authentication&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;All three tools solve the same problem — getting deployments through a firewall without opening inbound ports — but they target different audiences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DeployHQ&lt;/strong&gt; is the simplest path for teams that need protocol-flexible deployments with minimal infrastructure overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buddy&lt;/strong&gt; bundles deployment with CI/CD for teams that want everything in one platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Octopus Deploy&lt;/strong&gt; is enterprise release management for large-scale Windows/.NET environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are just trying to deploy code to a few servers behind a firewall, the &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; Agent is the fastest way to get there. Install one agent, whitelist your servers, and deploy — same workflow as any publicly accessible server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Get started with DeployHQ&lt;/a&gt; — the Agent is available on all plans, including the free tier. For setup instructions, see the &lt;a href="https://www.deployhq.com/support/network-agents" rel="noopener noreferrer"&gt;Agent documentation&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Questions about deploying to firewalled servers? Reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>deploymentagents</category>
      <category>deployhq</category>
      <category>buddy</category>
      <category>octopusdeploy</category>
    </item>
    <item>
      <title>How to Automate WordPress Deployments with Git (No More FTP)</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Mon, 02 Mar 2026 10:23:04 +0000</pubDate>
      <link>https://dev.to/deployhq/how-to-automate-wordpress-deployments-with-git-no-more-ftp-56h9</link>
      <guid>https://dev.to/deployhq/how-to-automate-wordpress-deployments-with-git-no-more-ftp-56h9</guid>
      <description>&lt;p&gt;If you're still deploying WordPress changes over FTP, you're one accidental overwrite away from a bad day. Manual file transfers don't track what changed, can't be rolled back, and fall apart the moment a second developer touches the project. The fix isn't complicated: put your WordPress theme and plugin code in Git, and let a deployment tool handle the rest.&lt;/p&gt;

&lt;p&gt;This guide walks through setting up automated Git-based deployments for WordPress — from structuring your repository to pushing changes to production with zero manual intervention.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why FTP Deployments Break Down
&lt;/h2&gt;

&lt;p&gt;FTP served WordPress well in 2010. In 2026, it's a liability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No change history.&lt;/strong&gt; You can't answer &lt;q&gt;what changed last Tuesday?&lt;/q&gt; when something breaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No rollback.&lt;/strong&gt; If a deployment introduces a bug, the only option is to re-upload the previous files — assuming you still have them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No collaboration.&lt;/strong&gt; Two developers editing the same theme via FTP will overwrite each other's work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No automation.&lt;/strong&gt; Every deployment requires a human clicking through FileZilla. That human will eventually drag the wrong folder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No staging environment.&lt;/strong&gt; You're deploying directly to production and hoping for the best.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Git-based deployments solve all five problems. Your code lives in a repository, every change is tracked, and deployments happen automatically when you push.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Track in Git (And What to Ignore)
&lt;/h2&gt;

&lt;p&gt;Not everything in a WordPress installation belongs in version control. The general rule: &lt;strong&gt;track code you write, ignore everything you install or generate.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended &lt;code&gt;.gitignore&lt;/code&gt; for WordPress
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# WordPress core — don't track, install via wp-cli or composer
&lt;/span&gt;/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;admin&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;includes&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-*.&lt;span class="n"&gt;php&lt;/span&gt;
/&lt;span class="n"&gt;index&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
/&lt;span class="n"&gt;license&lt;/span&gt;.&lt;span class="n"&gt;txt&lt;/span&gt;
/&lt;span class="n"&gt;readme&lt;/span&gt;.&lt;span class="n"&gt;html&lt;/span&gt;
/&lt;span class="n"&gt;xmlrpc&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;

&lt;span class="c"&gt;# wp-content: track selectively
&lt;/span&gt;/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;uploads&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;upgrade&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;cache&lt;/span&gt;/
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;advanced&lt;/span&gt;-&lt;span class="n"&gt;cache&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;object&lt;/span&gt;-&lt;span class="n"&gt;cache&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
/&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;content&lt;/span&gt;/&lt;span class="n"&gt;db&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;

&lt;span class="c"&gt;# Environment and secrets
&lt;/span&gt;.&lt;span class="n"&gt;env&lt;/span&gt;
&lt;span class="n"&gt;wp&lt;/span&gt;-&lt;span class="n"&gt;config&lt;/span&gt;-&lt;span class="n"&gt;local&lt;/span&gt;.&lt;span class="n"&gt;php&lt;/span&gt;
*.&lt;span class="n"&gt;sql&lt;/span&gt;
*.&lt;span class="n"&gt;sql&lt;/span&gt;.&lt;span class="n"&gt;gz&lt;/span&gt;

&lt;span class="c"&gt;# Dependencies
&lt;/span&gt;/&lt;span class="n"&gt;vendor&lt;/span&gt;/
/&lt;span class="n"&gt;node_modules&lt;/span&gt;/

&lt;span class="c"&gt;# OS files
&lt;/span&gt;.&lt;span class="n"&gt;DS_Store&lt;/span&gt;
&lt;span class="n"&gt;Thumbs&lt;/span&gt;.&lt;span class="n"&gt;db&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You Should Track
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wp-content/
├── themes/
│ └── your-custom-theme/ ← Track this
├── plugins/
│ └── your-custom-plugin/ ← Track this
└── mu-plugins/ ← Track this (must-use plugins)

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

&lt;/div&gt;



&lt;p&gt;If you use third-party plugins, manage them with &lt;a href="https://wpackagist.org/" rel="noopener noreferrer"&gt;Composer&lt;/a&gt; and track only the &lt;code&gt;composer.json&lt;/code&gt; and &lt;code&gt;composer.lock&lt;/code&gt; files — not the plugin source code itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Git for WordPress
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option A: Track Only Your Theme/Plugin
&lt;/h3&gt;

&lt;p&gt;The simplest approach — your Git repo contains only the code you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your-theme
git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit of theme"&lt;/span&gt;
git remote add origin git@github.com:yourteam/your-wp-theme.git
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main

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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; then deploys this repo to &lt;code&gt;/var/www/html/wp-content/themes/your-theme/&lt;/code&gt; on your server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option B: Track the Entire wp-content Directory
&lt;/h3&gt;

&lt;p&gt;For projects where you manage multiple themes, plugins, and mu-plugins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/wordpress/wp-content
git init
&lt;span class="c"&gt;# Add your .gitignore first&lt;/span&gt;
git add .gitignore
git add themes/your-theme/ plugins/your-plugin/ mu-plugins/
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial wp-content structure"&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option C: Full WordPress via Composer (Recommended for Teams)
&lt;/h3&gt;

&lt;p&gt;Use &lt;a href="https://roots.io/bedrock/" rel="noopener noreferrer"&gt;Roots Bedrock&lt;/a&gt; or a Composer-based WordPress setup for the most professional workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project roots/bedrock your-project
&lt;span class="nb"&gt;cd &lt;/span&gt;your-project
git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial Bedrock setup"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Bedrock gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress core managed via Composer (not tracked in Git)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.env&lt;/code&gt; file for environment-specific configuration&lt;/li&gt;
&lt;li&gt;Better directory structure separating web root from application code&lt;/li&gt;
&lt;li&gt;Plugin management via &lt;code&gt;composer require wpackagist-plugin/plugin-name&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Method 1: Automated Deployments with DeployHQ
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; connects your Git repository to your server and deploys automatically on every push. No scripts to write, no CI pipeline to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Project
&lt;/h3&gt;

&lt;p&gt;In your &lt;a href="https://deployhq.com" rel="noopener noreferrer"&gt;DeployHQ dashboard&lt;/a&gt;, create a new project and connect your GitHub, GitLab, or Bitbucket repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Add Your Server
&lt;/h3&gt;

&lt;p&gt;Add your production server using SSH (SFTP also works but SSH is faster and more reliable):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol:&lt;/strong&gt; SSH/SFTP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hostname:&lt;/strong&gt; your server IP or domain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username:&lt;/strong&gt; your deploy user (don't use root)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; SSH key (DeployHQ generates one — add the public key to your server's &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment path:&lt;/strong&gt; the target directory on your server, e.g.:

&lt;ul&gt;
&lt;li&gt;Theme only: &lt;code&gt;/var/www/html/wp-content/themes/your-theme&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Full wp-content: &lt;code&gt;/var/www/html/wp-content&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Bedrock: &lt;code&gt;/var/www/html/current&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Configure Build Steps
&lt;/h3&gt;

&lt;p&gt;Build steps run on DeployHQ's servers before files are transferred. Use them for:&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--optimize-autoloader&lt;/span&gt;
npm ci &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Compile assets:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# If your theme uses Webpack, Vite, or similar&lt;/span&gt;
npm run production

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Add Post-Deployment SSH Commands
&lt;/h3&gt;

&lt;p&gt;After files are transferred, run commands on your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clear WordPress object cache&lt;/span&gt;
wp cache flush &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/html

&lt;span class="c"&gt;# Clear OPcache (if using PHP-FPM)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reload php8.3-fpm

&lt;span class="c"&gt;# Run database migrations (if using a migration plugin)&lt;/span&gt;
wp core update-db &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/html

&lt;span class="c"&gt;# Clear CDN cache (example: Cloudflare)&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer CF_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"purge_everything":true}'&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Enable Automatic Deployments
&lt;/h3&gt;

&lt;p&gt;In your project settings, enable the webhook. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; will deploy automatically every time you push to the configured branch. You can also set up different branches for different servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; → Production server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;staging&lt;/code&gt; → Staging server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;develop&lt;/code&gt; → Development server&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 6: Use .deployignore
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.deployignore&lt;/code&gt; file in your repo root to exclude files that shouldn't be transferred to the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Development files
&lt;/span&gt;&lt;span class="n"&gt;node_modules&lt;/span&gt;/
&lt;span class="n"&gt;tests&lt;/span&gt;/
.&lt;span class="n"&gt;github&lt;/span&gt;/
.&lt;span class="n"&gt;gitignore&lt;/span&gt;
&lt;span class="n"&gt;README&lt;/span&gt;.&lt;span class="n"&gt;md&lt;/span&gt;
&lt;span class="n"&gt;package&lt;/span&gt;.&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="n"&gt;package&lt;/span&gt;-&lt;span class="n"&gt;lock&lt;/span&gt;.&lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="n"&gt;phpcs&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;
&lt;span class="n"&gt;phpunit&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="c"&gt;# Source files (only deploy compiled assets)
&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;scss&lt;/span&gt;/
&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;js&lt;/span&gt;/
&lt;span class="n"&gt;webpack&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;vite&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This keeps your deployment lean — only production-ready files reach the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 2: GitHub Actions + SSH
&lt;/h2&gt;

&lt;p&gt;For teams that want everything in their CI pipeline without a separate deployment tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy WordPress Theme&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build assets&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm ci&lt;/span&gt;
          &lt;span class="s"&gt;npm run build&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy via rsync&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;burnett01/rsync-deployments@7.0.1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;switches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-avz --delete --exclude='.git' --exclude='node_modules' --exclude='src/'&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./&lt;/span&gt;
          &lt;span class="na"&gt;remote_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/www/html/wp-content/themes/your-theme/&lt;/span&gt;
          &lt;span class="na"&gt;remote_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;remote_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;remote_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PRIVATE_KEY }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Post-deployment commands&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PRIVATE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;wp cache flush --path=/var/www/html&lt;/span&gt;
            &lt;span class="s"&gt;sudo systemctl reload php8.3-fpm&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This works, but you're maintaining the pipeline yourself. When something breaks at 2am, you're debugging YAML instead of deploying a fix. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles this infrastructure for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method 3: WP-CLI Deployment Scripts
&lt;/h2&gt;

&lt;p&gt;For power users who want full control via command-line scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"deploy@your-server.com"&lt;/span&gt;
&lt;span class="nv"&gt;DEPLOY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/www/html/wp-content/themes/your-theme"&lt;/span&gt;
&lt;span class="nv"&gt;BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Deploying WordPress theme ==="&lt;/span&gt;

&lt;span class="c"&gt;# Pre-flight checks&lt;/span&gt;
ssh &lt;span class="nv"&gt;$SERVER&lt;/span&gt; &lt;span class="s2"&gt;"test -d &lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deploy path missing"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Build locally&lt;/span&gt;
npm ci &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build

&lt;span class="c"&gt;# Sync files (excluding dev files)&lt;/span&gt;
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'.git'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'node_modules'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'src/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exclude&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'tests/'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  ./ &lt;span class="nv"&gt;$SERVER&lt;/span&gt;:&lt;span class="nv"&gt;$DEPLOY_PATH&lt;/span&gt;/

&lt;span class="c"&gt;# Post-deploy&lt;/span&gt;
ssh &lt;span class="nv"&gt;$SERVER&lt;/span&gt; &lt;span class="s2"&gt;"wp cache flush --path=/var/www/html &amp;amp;&amp;amp; sudo systemctl reload php8.3-fpm"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"=== Deployment complete ==="&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Simple, transparent, and portable. But it runs on your machine, so it doesn't work when you're offline, and other team members can't deploy without access to this script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling the Hard Part: Database Migrations
&lt;/h2&gt;

&lt;p&gt;WordPress doesn't have a built-in migration system like Rails or Laravel. Deploying code changes that require database schema updates needs careful handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 1: WP-CLI for Core Updates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# After deploying new WordPress core files&lt;/span&gt;
wp core update-db &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/html

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strategy 2: Migration Plugins
&lt;/h3&gt;

&lt;p&gt;Plugins like &lt;a href="https://deliciousbrains.com/wp-migrate-db-pro/" rel="noopener noreferrer"&gt;WP Migrate&lt;/a&gt; handle database syncing between environments. Run after code deployment to push schema changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strategy 3: Custom Migration Scripts
&lt;/h3&gt;

&lt;p&gt;For custom plugins that modify the database, use WordPress's &lt;code&gt;dbDelta()&lt;/code&gt; function in your plugin activation hook, or run WP-CLI commands post-deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wp &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s1"&gt;'your_plugin_run_migrations();'&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/www/html

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  What NOT to Do
&lt;/h3&gt;

&lt;p&gt;Never copy the production database to staging and then deploy staging code back to production. Data flows down (production → staging), code flows up (staging → production).&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-Environment Setup
&lt;/h2&gt;

&lt;p&gt;A professional WordPress workflow uses at least three environments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Local] --&amp;gt;|git push| B[Git Repository]
    B --&amp;gt;|auto-deploy| C[Staging]
    C --&amp;gt;|manual promote| D[Production]

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  In DeployHQ
&lt;/h3&gt;

&lt;p&gt;Create two servers in the same project:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Server&lt;/th&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Deploy Mode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Staging&lt;/td&gt;
&lt;td&gt;&lt;code&gt;staging&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Automatic on push&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;&lt;code&gt;main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Manual (one-click) or automatic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This gives you a safety net: code deploys to staging first, you verify it works, then merge to &lt;code&gt;main&lt;/code&gt; to push to production.&lt;/p&gt;

&lt;h3&gt;
  
  
  WordPress Configuration Per Environment
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;wp-config.php&lt;/code&gt; with environment detection, or better yet, use a &lt;code&gt;.env&lt;/code&gt; file (native with Bedrock):&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="c1"&gt;// wp-config.php approach&lt;/span&gt;
&lt;span class="nv"&gt;$env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_ENV'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$env&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'staging'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_DEBUG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WP_DEBUG_LOG'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'DISALLOW_FILE_MODS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  WordPress-Specific .deployignore Patterns
&lt;/h2&gt;

&lt;p&gt;Beyond the basics, these WordPress-specific exclusions keep your deployments clean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# WordPress dev/test files
&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;/
&lt;span class="n"&gt;phpunit&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;.&lt;span class="n"&gt;dist&lt;/span&gt;
&lt;span class="n"&gt;phpcs&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;.&lt;span class="n"&gt;dist&lt;/span&gt;
.&lt;span class="n"&gt;phpcs&lt;/span&gt;.&lt;span class="n"&gt;xml&lt;/span&gt;

&lt;span class="c"&gt;# Theme development
&lt;/span&gt;&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;scss&lt;/span&gt;/
&lt;span class="n"&gt;src&lt;/span&gt;/&lt;span class="n"&gt;js&lt;/span&gt;/
&lt;span class="n"&gt;webpack&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;vite&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;tailwind&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;postcss&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;
&lt;span class="n"&gt;babel&lt;/span&gt;.&lt;span class="n"&gt;config&lt;/span&gt;.&lt;span class="n"&gt;js&lt;/span&gt;

&lt;span class="c"&gt;# Documentation
&lt;/span&gt;*.&lt;span class="n"&gt;md&lt;/span&gt;
&lt;span class="n"&gt;LICENSE&lt;/span&gt;
&lt;span class="n"&gt;CHANGELOG&lt;/span&gt;

&lt;span class="c"&gt;# CI/CD config (handled by DeployHQ, not needed on server)
&lt;/span&gt;.&lt;span class="n"&gt;github&lt;/span&gt;/
.&lt;span class="n"&gt;gitlab&lt;/span&gt;-&lt;span class="n"&gt;ci&lt;/span&gt;.&lt;span class="n"&gt;yml&lt;/span&gt;
&lt;span class="n"&gt;bitbucket&lt;/span&gt;-&lt;span class="n"&gt;pipelines&lt;/span&gt;.&lt;span class="n"&gt;yml&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tracking wp-config.php in Git.&lt;/strong&gt; This file contains database passwords. Use &lt;code&gt;.env&lt;/code&gt; files or environment-specific config files that are in &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deploying node_modules.&lt;/strong&gt; Your build step should compile assets. Only deploy the compiled CSS/JS, never the 200MB of npm packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using root for SSH deployments.&lt;/strong&gt; Create a dedicated deploy user with limited permissions. It should own the web directory and nothing else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skipping staging.&lt;/strong&gt; &lt;q&gt;It works on my machine&lt;/q&gt; is not a deployment strategy. Always verify on staging first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to flush cache.&lt;/strong&gt; WordPress aggressively caches everything. A deployment without a cache clear means users see stale content.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Each Method
&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;DeployHQ&lt;/th&gt;
&lt;th&gt;GitHub Actions&lt;/th&gt;
&lt;th&gt;WP-CLI Script&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Setup time&lt;/td&gt;
&lt;td&gt;10 minutes&lt;/td&gt;
&lt;td&gt;1-2 hours&lt;/td&gt;
&lt;td&gt;30 minutes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance&lt;/td&gt;
&lt;td&gt;None (managed)&lt;/td&gt;
&lt;td&gt;You maintain YAML&lt;/td&gt;
&lt;td&gt;You maintain script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback&lt;/td&gt;
&lt;td&gt;One-click&lt;/td&gt;
&lt;td&gt;Manual revert&lt;/td&gt;
&lt;td&gt;Manual revert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-server&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;DIY per environment&lt;/td&gt;
&lt;td&gt;DIY per server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team access&lt;/td&gt;
&lt;td&gt;Web dashboard&lt;/td&gt;
&lt;td&gt;Repo access required&lt;/td&gt;
&lt;td&gt;CLI access required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build steps&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;GitHub-hosted runners&lt;/td&gt;
&lt;td&gt;Your machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;&lt;a href="https://deployhq.com/pricing" rel="noopener noreferrer"&gt;Free tier available&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Free for public repos&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;Teams, agencies, multiple sites&lt;/td&gt;
&lt;td&gt;Dev teams already using GH Actions&lt;/td&gt;
&lt;td&gt;Solo devs, simple setups&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;The fastest path from FTP to automated deployments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Put your theme/plugin code in Git (10 minutes)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deployhq.com/signup" rel="noopener noreferrer"&gt;Create a free&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; project (5 minutes)&lt;/li&gt;
&lt;li&gt;Connect your repo and add your server (5 minutes)&lt;/li&gt;
&lt;li&gt;Push a change and watch it deploy automatically&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No YAML to debug, no scripts to maintain, no FTP clients to open. Your WordPress deployments now work like every other modern web project.&lt;/p&gt;




&lt;p&gt;If you run into issues, reach out to our team at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>tutorials</category>
      <category>wordpress</category>
      <category>deployments</category>
    </item>
    <item>
      <title>AI Code Review as Your Last Line of Defense Before Deployment</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Fri, 27 Feb 2026 15:44:24 +0000</pubDate>
      <link>https://dev.to/deployhq/ai-code-review-as-your-last-line-of-defense-before-deployment-1859</link>
      <guid>https://dev.to/deployhq/ai-code-review-as-your-last-line-of-defense-before-deployment-1859</guid>
      <description>&lt;p&gt;&lt;em&gt;Catching security vulnerabilities, performance issues, and misconfigurations before code reaches production.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Code review is one of the most valuable practices in software development. A second pair of eyes catches bugs, improves code quality, and spreads knowledge across the team. But traditional code review has limitations: reviewers get tired, miss edge cases, and can't possibly remember every security best practice.&lt;/p&gt;

&lt;p&gt;What if you had a reviewer who never got tired, had encyclopedic knowledge of security vulnerabilities, and could analyse every line of every commit before it reached production?&lt;/p&gt;

&lt;p&gt;AI-powered code review is becoming that reviewer—and it's emerging as a critical last line of defense in the deployment pipeline. This is part of the broader shift toward &lt;a href="https://dev.to/deployhq/how-ai-coding-assistants-are-changing-the-way-we-deploy-code-1iic"&gt;AI-integrated deployment workflows&lt;/a&gt; that's transforming how we ship code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gap in Traditional Code Review
&lt;/h2&gt;

&lt;p&gt;Human code reviewers are excellent at many things: evaluating architecture decisions, ensuring code readability, and mentoring junior developers. But they're not ideal for everything:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Human Reviewers Excel At&lt;/th&gt;
&lt;th&gt;Human Reviewers Miss&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Architecture decisions&lt;/td&gt;
&lt;td&gt;Subtle security vulnerabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code readability&lt;/td&gt;
&lt;td&gt;Dependency vulnerabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business logic correctness&lt;/td&gt;
&lt;td&gt;Performance regressions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mentoring and teaching&lt;/td&gt;
&lt;td&gt;Configuration drift&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context and intent&lt;/td&gt;
&lt;td&gt;Consistent enforcement&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A human reviewer might catch that a function is poorly named. They're less likely to notice that a dependency was updated to a version with a known security vulnerability, or that a new SQL query is vulnerable to injection in an edge case.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    subgraph Traditional
        A[Write Code] --&amp;gt; B[Human Review]
        B --&amp;gt; C[Deploy]
        C --&amp;gt; D[Discover Issues in Production]
    end

    subgraph AI-Augmented
        E[Write Code] --&amp;gt; F[Human Review]
        F --&amp;gt; G[AI Security/Performance Review]
        G --&amp;gt; H[Deploy]
        H --&amp;gt; I[Fewer Production Issues]
    end

    style A fill:#64748B,color:#fff
    style B fill:#64748B,color:#fff
    style C fill:#64748B,color:#fff
    style D fill:#EF4444,color:#fff
    style E fill:#64748B,color:#fff
    style F fill:#64748B,color:#fff
    style G fill:#0891B2,color:#fff
    style H fill:#64748B,color:#fff
    style I fill:#10B981,color:#fff

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Want to catch issues earlier?&lt;/strong&gt; &lt;a href="https://www.deployhq.com/features/build-pipelines" rel="noopener noreferrer"&gt;DeployHQ's build pipelines&lt;/a&gt; can run tests, linters, and security scans before deployment—stopping bad code before it reaches your servers.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What AI Code Review Catches
&lt;/h2&gt;

&lt;p&gt;AI code review is particularly effective at catching issues that require pattern matching across large codebases or knowledge of external factors:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Security Vulnerabilities
&lt;/h3&gt;

&lt;p&gt;AI can identify common vulnerability patterns that humans might overlook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AI would flag this SQL injection vulnerability&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"name LIKE '%&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# VULNERABLE: SQL Injection risk&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# AI suggests this fix&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search_users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"name LIKE ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"%&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# SAFE: Parameterised query&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Common security issues AI catches:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vulnerability Type&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Why AI Catches It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQL Injection&lt;/td&gt;
&lt;td&gt;String interpolation in queries&lt;/td&gt;
&lt;td&gt;Pattern matching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XSS&lt;/td&gt;
&lt;td&gt;Unescaped user input in templates&lt;/td&gt;
&lt;td&gt;Output context analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardcoded Secrets&lt;/td&gt;
&lt;td&gt;API keys in code&lt;/td&gt;
&lt;td&gt;Pattern recognition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insecure Dependencies&lt;/td&gt;
&lt;td&gt;Known CVEs in packages&lt;/td&gt;
&lt;td&gt;Database lookup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth Bypass&lt;/td&gt;
&lt;td&gt;Missing authentication checks&lt;/td&gt;
&lt;td&gt;Control flow analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Performance Regressions
&lt;/h3&gt;

&lt;p&gt;AI can spot code patterns that will cause performance issues at scale:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AI would flag this N+1 query&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dashboard&lt;/span&gt;
  &lt;span class="vi"&gt;@posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
  &lt;span class="c1"&gt;# In the view: @posts.each { |p| p.author.name }&lt;/span&gt;
  &lt;span class="c1"&gt;# PROBLEM: This will execute 1 + N queries&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# AI suggests eager loading&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dashboard&lt;/span&gt;
  &lt;span class="vi"&gt;@posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:author&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt; &lt;span class="c1"&gt;# FIXED: Single query with JOIN&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Configuration Issues
&lt;/h3&gt;

&lt;p&gt;Before deployment, AI can verify that all required configuration is in place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AI reviews deployment config and catches:&lt;/span&gt;

&lt;span class="c1"&gt;# WARNING: Missing in production but present in staging&lt;/span&gt;
&lt;span class="na"&gt;environment_variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;staging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DATABASE_URL&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REDIS_URL&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;STRIPE_SECRET_KEY&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;NEW_RELIC_LICENSE&lt;/span&gt; &lt;span class="c1"&gt;# Present in staging&lt;/span&gt;
  &lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DATABASE_URL&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;REDIS_URL&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;STRIPE_SECRET_KEY&lt;/span&gt;
    &lt;span class="c1"&gt;# NEW_RELIC_LICENSE missing! AI flags this&lt;/span&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Environment variable management made easy:&lt;/strong&gt; &lt;a href="https://www.deployhq.com/features" rel="noopener noreferrer"&gt;DeployHQ's environment variables&lt;/a&gt; let you manage secrets securely across all your environments from one dashboard.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Breaking Changes
&lt;/h3&gt;

&lt;p&gt;AI can analyse API changes and flag potential breaking changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Previous version&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# New version - AI flags breaking change&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;shipping_address&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt; &lt;span class="c1"&gt;# WARNING: New required param&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# AI suggests backward compatibility&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="ss"&gt;shipping_address: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# SAFE: Optional param&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Integrating AI Review into Your Deployment Pipeline
&lt;/h2&gt;

&lt;p&gt;AI code review works best when integrated directly into your deployment workflow. Here's how to structure it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Push to Branch] --&amp;gt; B[CI Tests]
    B --&amp;gt; C[Human PR Review]
    C --&amp;gt; D[AI Security Review]
    D --&amp;gt; E{Issues Found?}
    E --&amp;gt;|Yes| F[Block + Report]
    E --&amp;gt;|No| G[Deploy to Staging]
    G --&amp;gt; H[AI Production Readiness Check]
    H --&amp;gt; I[Deploy to Production]

    style A fill:#64748B,color:#fff
    style B fill:#64748B,color:#fff
    style C fill:#64748B,color:#fff
    style D fill:#0891B2,color:#fff
    style E fill:#F59E0B,color:#fff
    style F fill:#EF4444,color:#fff
    style G fill:#10B981,color:#fff
    style H fill:#0891B2,color:#fff
    style I fill:#10B981,color:#fff

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

&lt;/div&gt;



&lt;p&gt;DeployHQ's &lt;a href="https://www.deployhq.com/features/build-pipelines" rel="noopener noreferrer"&gt;build commands&lt;/a&gt; can run at each stage—executing security scans, running tests, and blocking deployments that don't pass.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-Deployment Checklist: AI Edition
&lt;/h3&gt;

&lt;p&gt;Configure your AI review to check for these categories before each deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ai_review_config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sql_injection&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;xss_vulnerabilities&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;hardcoded_secrets&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;insecure_dependencies&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;authentication_bypass&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;authorization_issues&lt;/span&gt;

  &lt;span class="na"&gt;performance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;n_plus_one_queries&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;missing_indexes&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;memory_leaks&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;inefficient_loops&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;large_payload_responses&lt;/span&gt;

  &lt;span class="na"&gt;reliability&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;error_handling&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;null_pointer_risks&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;race_conditions&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;resource_cleanup&lt;/span&gt;

  &lt;span class="na"&gt;configuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;missing_env_vars&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;config_drift_between_environments&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deprecated_settings&lt;/span&gt;

  &lt;span class="na"&gt;compatibility&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;breaking_api_changes&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database_migration_risks&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dependency_conflicts&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Practical Implementation
&lt;/h2&gt;

&lt;p&gt;Here's how to implement AI code review in your workflow today:&lt;/p&gt;

&lt;h3&gt;
  
  
  Using AI Assistants for Manual Review
&lt;/h3&gt;

&lt;p&gt;Before deploying, you can ask an AI assistant to review your changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**Prompt for Pre-Deployment Review:**

Review the following code changes for security vulnerabilities, 
performance issues, and potential bugs. Focus on:

1. SQL injection or XSS vulnerabilities
2. N+1 queries or performance regressions  
3. Missing error handling
4. Breaking API changes
5. Security best practices

Changes:
[PASTE DIFF OR CODE HERE]

For each issue found, provide:
- Severity (Critical/High/Medium/Low)
- Location (file and line)
- Description of the issue
- Recommended fix with code example

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sample AI Review Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;## AI Code Review Results&lt;/span&gt;

&lt;span class="c1"&gt;### Critical Issues (1)&lt;/span&gt;

&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="no"&gt;SQL&lt;/span&gt; &lt;span class="no"&gt;Injection&lt;/span&gt; &lt;span class="no"&gt;Vulnerability&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sb"&gt;`app/models/search.rb`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="no"&gt;Issue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;User&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="n"&gt;directly&lt;/span&gt; &lt;span class="n"&gt;interpolated&lt;/span&gt; &lt;span class="n"&gt;into&lt;/span&gt; &lt;span class="no"&gt;SQL&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="no"&gt;Fix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
ruby&lt;br&gt;
  # Before (vulnerable)&lt;br&gt;
  where("title LIKE '%#{params[:q]}%'")&lt;/p&gt;

&lt;p&gt;# After (safe)&lt;br&gt;
  where("title LIKE ?", "%#{sanitize_sql_like(params[:q])}%")&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
### Medium Issues (2)

**N+1 Query**

- File: `app/controllers/posts_controller.rb`, line 45
- Issue: Loading comments without eager loading
- Impact: ~50ms additional latency per post
- Fix: Add `.includes(:comments)` to the query

**Missing Error Handling**

- File: `app/services/payment_processor.rb`, line 78
- Issue: External API call without rescue block
- Fix: Wrap in begin/rescue and handle timeout/connection errors

### Suggestions (1)

**Consider Adding Index**

- File: `db/migrate/20240115_add_status_to_orders.rb`
- Suggestion: The `status` column is queried frequently. Consider adding an index for better performance.

* * *

**Summary:** 1 critical issue must be fixed before deployment.

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

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
ruby&lt;/p&gt;

&lt;p&gt;When issues are found post-deployment, &lt;a href="https://www.deployhq.com/blog/ai-deployment-troubleshooting" rel="noopener noreferrer"&gt;AI-powered troubleshooting&lt;/a&gt; can help you quickly identify the root cause and fix it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building Automated AI Gates
&lt;/h2&gt;

&lt;p&gt;For teams ready to automate, here's a pattern for implementing AI review gates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Example: Pre-deployment AI review hook&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeploymentReview&lt;/span&gt;
  &lt;span class="no"&gt;SEVERITY_THRESHOLDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="ss"&gt;staging: :high&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Block on high+ severity&lt;/span&gt;
    &lt;span class="ss"&gt;production: :medium&lt;/span&gt; &lt;span class="c1"&gt;# Block on medium+ severity&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:,&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;:)&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;AIReviewer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;analyse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SEVERITY_THRESHOLDS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;blocking_issues&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;blocking_issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;status: :blocked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;reason: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;blocking_issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; issues require attention"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;issues: &lt;/span&gt;&lt;span class="n"&gt;blocking_issues&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;report_url: &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_report_url&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="ss"&gt;status: :approved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;warnings: &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;issues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="ss"&gt;report_url: &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;full_report_url&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Automate your quality gates:&lt;/strong&gt; DeployHQ's &lt;a href="https://www.deployhq.com/features" rel="noopener noreferrer"&gt;deployment conditions&lt;/a&gt; can block deployments when tests fail or when specific files change—add AI review as another gate in your pipeline.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What AI Review Doesn't Replace
&lt;/h2&gt;

&lt;p&gt;AI code review is powerful, but it complements rather than replaces other practices:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Still Need Humans For&lt;/th&gt;
&lt;th&gt;AI Handles Well&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Architecture decisions&lt;/td&gt;
&lt;td&gt;Pattern-based vulnerabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business logic review&lt;/td&gt;
&lt;td&gt;Dependency security&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code style preferences&lt;/td&gt;
&lt;td&gt;Consistent rule enforcement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mentoring developers&lt;/td&gt;
&lt;td&gt;Exhaustive coverage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context-dependent decisions&lt;/td&gt;
&lt;td&gt;Known vulnerability databases&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The ideal setup uses both: human reviewers for high-level decisions and AI reviewers for systematic, exhaustive checks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
    subgraph HumanReview
        H1[Architecture]
        H2[Business Logic]
        H3[Readability]
        H4[Mentoring]
    end

    subgraph AIReview
        A1[Security Patterns]
        A2[Performance Issues]
        A3[Dependency Risks]
        A4[Configuration Gaps]
    end

    subgraph Both
        B1[Critical Business Logic]
        B2[Complex Security Decisions]
    end

    style H1 fill:#64748B,color:#fff
    style H2 fill:#64748B,color:#fff
    style H3 fill:#64748B,color:#fff
    style H4 fill:#64748B,color:#fff
    style A1 fill:#0891B2,color:#fff
    style A2 fill:#0891B2,color:#fff
    style A3 fill:#0891B2,color:#fff
    style A4 fill:#0891B2,color:#fff
    style B1 fill:#10B981,color:#fff
    style B2 fill:#10B981,color:#fff

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Measuring AI Review Effectiveness
&lt;/h2&gt;

&lt;p&gt;Track these metrics to measure the value of AI code review:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What to Track&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Issues Caught Pre-Production&lt;/td&gt;
&lt;td&gt;Security vulns, perf issues, config gaps&lt;/td&gt;
&lt;td&gt;Direct prevention value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production Incident Reduction&lt;/td&gt;
&lt;td&gt;Before/after AI implementation&lt;/td&gt;
&lt;td&gt;Business impact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False Positive Rate&lt;/td&gt;
&lt;td&gt;AI flags that weren't real issues&lt;/td&gt;
&lt;td&gt;Review efficiency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to Deploy&lt;/td&gt;
&lt;td&gt;PR merge to production&lt;/td&gt;
&lt;td&gt;Pipeline speed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Getting Started Today
&lt;/h2&gt;

&lt;p&gt;You don't need sophisticated tooling to start with AI code review. Here's a progression:&lt;/p&gt;

&lt;h3&gt;
  
  
  Week 1: Manual AI Reviews
&lt;/h3&gt;

&lt;p&gt;Before each deployment, paste your diff into an AI assistant with the review prompt above. Track what it finds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Month 1: Standardised Process
&lt;/h3&gt;

&lt;p&gt;Create a checklist that includes AI review. Document the prompts your team uses. Start tracking metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Month 3: Automated Integration
&lt;/h3&gt;

&lt;p&gt;Integrate AI review into your CI/CD pipeline. Set up automated blocking rules. Create dashboards for tracking.&lt;/p&gt;

&lt;p&gt;Looking ahead, this AI review will integrate with &lt;a href="https://dev.to/deployhq/the-rise-of-conversational-deployments-will-we-deploy-with-natural-language-2pib-temp-slug-9030059"&gt;conversational deployment&lt;/a&gt;—you'll just say &lt;q&gt;deploy if the AI review passes&lt;/q&gt; and it'll happen automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;AI code review is becoming an essential part of the deployment pipeline—a tireless reviewer that catches what humans miss. Here's what to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI excels at pattern-based security checks, performance analysis, and configuration verification&lt;/li&gt;
&lt;li&gt;Human reviewers remain essential for architecture, business logic, and mentoring&lt;/li&gt;
&lt;li&gt;Start with manual AI reviews before automating&lt;/li&gt;
&lt;li&gt;Configure severity thresholds differently for staging vs production&lt;/li&gt;
&lt;li&gt;Track metrics to measure effectiveness and identify gaps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The question isn't whether to add AI to your code review process—it's how quickly you can implement it before your next production incident.&lt;/p&gt;




&lt;h2&gt;
  
  
  Continue Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/deployhq/how-ai-coding-assistants-are-changing-the-way-we-deploy-code-1iic"&gt;How AI Coding Assistants Are Changing Deployments&lt;/a&gt; — The big picture&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/deployhq/ai-powered-deployment-troubleshooting-from-error-logs-to-fixes-in-seconds-1a52"&gt;AI-Powered Deployment Troubleshooting&lt;/a&gt; — When issues slip through&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/deployhq/the-rise-of-conversational-deployments-will-we-deploy-with-natural-language-2pib-temp-slug-9030059"&gt;The Rise of Conversational Deployments&lt;/a&gt; — &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; with natural language&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/deployhq/mcp-and-the-future-of-ai-integrated-devops-tools-29fk"&gt;MCP and the Future of AI-Integrated DevOps&lt;/a&gt; — The technical foundation&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Ready to add more safety to your deployments?&lt;/strong&gt; &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Start your free&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; trial and configure build pipelines that run tests and scans before every deployment.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have ideas for AI deployment features? &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Tell us on X&lt;/a&gt; — we'd love to hear what you'd build.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>infrastructure</category>
      <category>codereview</category>
    </item>
    <item>
      <title>OpenTelemetry in Practice: Setting Up Metrics, Traces, and Logs for Your Applications</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Wed, 25 Feb 2026 17:40:18 +0000</pubDate>
      <link>https://dev.to/deployhq/opentelemetry-in-practice-setting-up-metrics-traces-and-logs-for-your-applications-1hof</link>
      <guid>https://dev.to/deployhq/opentelemetry-in-practice-setting-up-metrics-traces-and-logs-for-your-applications-1hof</guid>
      <description>&lt;p&gt;Your deployment pipeline is green, the health check returns 200, and users are reporting that &lt;q&gt;the site is slow.&lt;/q&gt; You check the server — CPU is fine, memory is fine, disk is fine. So where's the problem?&lt;/p&gt;

&lt;p&gt;This is the observability gap. Most teams have some logging, maybe a dashboard showing CPU and memory, but no coherent way to answer the question that actually matters during an incident: &lt;em&gt;what is happening, right now, across all the services involved in this request?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opentelemetry.io" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; has become the industry standard for closing that gap. It's a CNCF project (the second most active after Kubernetes) that provides a single, vendor-neutral framework for collecting metrics, traces, and logs from any application — regardless of language, framework, or infrastructure. This guide covers what OpenTelemetry actually is, how to set it up, and how to connect it to your deployment pipeline so you always know whether the last release caused the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Pillars — and Why You Need All of Them
&lt;/h2&gt;

&lt;p&gt;Observability rests on three signal types, each answering a different question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Metrics&lt;/strong&gt; answer &lt;q&gt;is something wrong?&lt;/q&gt; They're aggregated numbers — request count, error rate, response time percentiles, CPU usage, queue depth. Metrics are cheap to collect, cheap to store, and the foundation of alerting. When your PagerDuty goes off at 2 AM, it's because a metric crossed a threshold.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traces&lt;/strong&gt; answer &lt;q&gt;where is it slow?&lt;/q&gt; A distributed trace follows a single request as it moves across services, databases, message queues, and external APIs. Each operation is a &lt;q&gt;span&lt;/q&gt; with a start time, duration, and metadata. When a user reports that checkout is slow, traces show you that 800ms of a 1.2s request is spent waiting for the payment gateway.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logs&lt;/strong&gt; answer &lt;q&gt;why did it break?&lt;/q&gt; Logs provide the detailed context that metrics and traces can't: the exact SQL query that failed, the malformed JSON payload, the specific validation error. The key is correlating logs with traces — when a trace shows a slow span, the correlated logs explain what happened during that span.&lt;/p&gt;

&lt;p&gt;Without all three, you're debugging with partial information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Metrics without traces: you know &lt;em&gt;something&lt;/em&gt; is slow, but not &lt;em&gt;where&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Traces without logs: you know &lt;em&gt;where&lt;/em&gt; it's slow, but not &lt;em&gt;why&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Logs without traces: you have detail but can't connect it across services&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why OpenTelemetry Won
&lt;/h2&gt;

&lt;p&gt;Before OpenTelemetry, every observability vendor had its own instrumentation library. If you used Datadog, you installed the Datadog agent. If you used New Relic, you installed their SDK. Switching vendors meant re-instrumenting your entire application.&lt;/p&gt;

&lt;p&gt;OpenTelemetry solves this by separating &lt;strong&gt;instrumentation&lt;/strong&gt; (collecting telemetry from your application) from &lt;strong&gt;export&lt;/strong&gt; (sending it to a backend). You instrument once with OpenTelemetry, then export to whatever backend you choose — Grafana, Datadog, New Relic, Honeycomb, Jaeger, or any combination. Switch backends without changing a line of application code.&lt;/p&gt;

&lt;p&gt;The project provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language SDKs&lt;/strong&gt; for most major languages (Python, JavaScript/Node.js, Java, Go, .NET, Ruby, PHP, Rust)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-instrumentation agents&lt;/strong&gt; that instrument popular libraries without code changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The OpenTelemetry Collector&lt;/strong&gt; — a proxy that receives, processes, and exports telemetry data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic conventions&lt;/strong&gt; — standardised attribute names so &lt;code&gt;http.request.method&lt;/code&gt; means the same thing everywhere&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started: The Two Approaches
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Zero-Code Auto-Instrumentation
&lt;/h3&gt;

&lt;p&gt;The fastest way to start is auto-instrumentation — agents or libraries that hook into your runtime and automatically instrument HTTP requests, database queries, and other common operations without changing your application code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Run your app with auto-instrumentation&lt;/span&gt;
opentelemetry-instrument &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service_name&lt;/span&gt; my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--exporter_otlp_endpoint&lt;/span&gt; http://collector:4317 &lt;span class="se"&gt;\&lt;/span&gt;
  python app.py

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

&lt;/div&gt;



&lt;p&gt;This automatically instruments Flask, Django, FastAPI, SQLAlchemy, requests, urllib3, psycopg2, and dozens of other Python libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node.js:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;npm&lt;/span&gt; &lt;span class="nx"&gt;install&lt;/span&gt; &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;opentelemetry&lt;/span&gt;&lt;span class="sr"&gt;/auto-instrumentations-node &lt;/span&gt;&lt;span class="err"&gt;\
&lt;/span&gt;  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;opentelemetry&lt;/span&gt;&lt;span class="sr"&gt;/sdk-node @opentelemetry/&lt;/span&gt;&lt;span class="nx"&gt;exporter&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;otlp&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;grpc&lt;/span&gt;


&lt;span class="c1"&gt;// tracing.js — require this BEFORE your application&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NodeSDK&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/sdk-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getNodeAutoInstrumentations&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/auto-instrumentations-node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OTLPTraceExporter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/exporter-trace-otlp-grpc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NodeSDK&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;traceExporter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OTLPTraceExporter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://collector:4317&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;instrumentations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;getNodeAutoInstrumentations&lt;/span&gt;&lt;span class="p"&gt;()],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;sdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


&lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;tracing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This instruments Express, Fastify, Koa, pg, mysql2, mongodb, Redis, HTTP client calls, and more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download the agent JAR&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; opentelemetry-javaagent.jar &lt;span class="se"&gt;\&lt;/span&gt;
  https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar

&lt;span class="c"&gt;# Run with the agent attached&lt;/span&gt;
java &lt;span class="nt"&gt;-javaagent&lt;/span&gt;:opentelemetry-javaagent.jar &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-Dotel&lt;/span&gt;.service.name&lt;span class="o"&gt;=&lt;/span&gt;my-app &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-Dotel&lt;/span&gt;.exporter.otlp.endpoint&lt;span class="o"&gt;=&lt;/span&gt;http://collector:4317 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-jar&lt;/span&gt; app.jar

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

&lt;/div&gt;



&lt;p&gt;The Java agent supports over 100 libraries — Spring, Quarkus, JDBC, Hibernate, Kafka, gRPC, and more. It uses bytecode manipulation, so no code changes are needed at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What auto-instrumentation costs:&lt;/strong&gt; typically 2-5% runtime overhead and 5-15% on startup time. For most applications, this is negligible. For latency-critical hot paths, you can selectively disable specific instrumentations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual SDK Instrumentation
&lt;/h3&gt;

&lt;p&gt;Auto-instrumentation covers infrastructure operations (HTTP, databases, messaging) but can't instrument your business logic. If you want to trace &lt;q&gt;calculate shipping cost&lt;/q&gt; or count &lt;q&gt;orders placed per minute,&lt;/q&gt; you need the SDK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;opentelemetry&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;

&lt;span class="n"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_tracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order-service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;meter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_meter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order-service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;orders_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;meter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_counter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;orders.placed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total orders placed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;place_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_as_current_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;place_order&lt;/span&gt;&lt;span class="sh"&gt;"&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;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order.items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order.customer_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;orders_counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order.id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record_exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Node.js example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@opentelemetry/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTracer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order-service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ordersCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;meter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orders.placed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tracer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startActiveSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;place_order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order.items&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order.customer_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;ordersCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;order.id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recordException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SpanStatusCode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ERROR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Auto-instrumentation and manual instrumentation work together seamlessly. The auto-instrumented spans (HTTP, database) and your custom business spans are linked into the same trace automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Collector: Your Telemetry Router
&lt;/h2&gt;

&lt;p&gt;The OpenTelemetry Collector is a proxy that sits between your applications and your observability backend. Applications send telemetry to the collector, and the collector routes it to whichever backends you use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# otel-collector-config.yaml&lt;/span&gt;
&lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;protocols&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;grpc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4317&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:4318&lt;/span&gt;

&lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
    &lt;span class="na"&gt;send_batch_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1024&lt;/span&gt;

&lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;prometheus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.0.0.0:8889&lt;/span&gt;
  &lt;span class="na"&gt;otlp/traces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tempo:4317&lt;/span&gt;
    &lt;span class="na"&gt;tls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;insecure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;loki&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://loki:3100/loki/api/v1/push&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;traces&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp/traces&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;prometheus&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;logs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;receivers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;otlp&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;batch&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;exporters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;loki&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why not export directly from your application?&lt;/strong&gt; Three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Decoupling.&lt;/strong&gt; Your applications don't need to know about your observability backend. Switch from Jaeger to Grafana Tempo? Change the collector config, not your application code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processing.&lt;/strong&gt; The collector can sample, filter, enrich, and batch telemetry before forwarding — reducing storage costs and noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability.&lt;/strong&gt; The collector buffers data during backend outages. Direct export risks dropping telemetry when the backend is unreachable.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sampling: Don't Trace Everything in Production
&lt;/h2&gt;

&lt;p&gt;Tracing every request in production generates enormous volumes of data. A service handling 1,000 requests per second produces 86 million traces per day. At approximately 1 KB per span with a 5-span average trace, that's ~430 GB/day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Head-based sampling&lt;/strong&gt; decides at the start of a trace whether to record it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Environment variable — sample 10% of traces&lt;/span&gt;
&lt;span class="nv"&gt;OTEL_TRACES_SAMPLER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;parentbased_traceidratio
&lt;span class="nv"&gt;OTEL_TRACES_SAMPLER_ARG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.1

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Tail-based sampling&lt;/strong&gt; (configured in the collector) decides after the trace completes, allowing you to keep all error traces and slow traces while sampling normal traffic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;processors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tail_sampling&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;errors&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;status_code&lt;/span&gt;
        &lt;span class="na"&gt;status_code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;status_codes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ERROR&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;slow-traces&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latency&lt;/span&gt;
        &lt;span class="na"&gt;latency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;threshold_ms&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;1000&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;baseline&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;probabilistic&lt;/span&gt;
        &lt;span class="na"&gt;probabilistic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;sampling_percentage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;10&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This keeps 100% of errors and slow requests (the ones you actually need to debug) while sampling 10% of healthy traffic. In practice, this reduces trace storage by 80-90% while preserving diagnostic value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting Observability to Your Deployment Pipeline
&lt;/h2&gt;

&lt;p&gt;Observability becomes most valuable when it's connected to your deployment pipeline. Correlating deployments with metric changes answers the most common production question: &lt;q&gt;did the last deployment cause this?&lt;/q&gt;&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, you can use post-deployment commands or &lt;a href="https://www.deployhq.com/blog/deployments-with-deployhq-integrations" rel="noopener noreferrer"&gt;webhooks&lt;/a&gt; to annotate your monitoring dashboards whenever a deployment completes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Post-deploy command: annotate Grafana with deployment event&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://grafana:3000/api/annotations &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$GRAFANA_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Deployed &lt;/span&gt;&lt;span class="nv"&gt;$DEPLOYHQ_REVISION&lt;/span&gt;&lt;span class="s2"&gt; to &lt;/span&gt;&lt;span class="nv"&gt;$DEPLOYHQ_SERVER&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
    &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;tags&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: [&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;deployment&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOYHQ_PROJECT&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;]
  }"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;When you see a metric anomaly on a dashboard, the deployment annotation immediately tells you whether it correlates with a release — saving the first 10 minutes of every incident investigation.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://dev.to/deployhq/zero-downtime-deployments-keeping-your-application-running-smoothly-hia"&gt;zero-downtime deployments&lt;/a&gt;, observability is especially critical. You need to verify that the new version is healthy &lt;em&gt;before&lt;/em&gt; the old version is taken down. Health check endpoints should go beyond a simple HTTP 200:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Flask example — readiness probe that checks dependencies
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/health/ready&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;readiness&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;checks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SELECT 1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;redis_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cache&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cache&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;503&lt;/span&gt;

    &lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ready&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checks&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Practical Starting Stack
&lt;/h2&gt;

&lt;p&gt;If you're starting from zero, here's a realistic observability stack that works for small-to-medium deployments without enterprise licensing costs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instrumentation&lt;/td&gt;
&lt;td&gt;OpenTelemetry SDK + Auto-instrumentation&lt;/td&gt;
&lt;td&gt;Collect traces, metrics, logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collector&lt;/td&gt;
&lt;td&gt;OpenTelemetry Collector&lt;/td&gt;
&lt;td&gt;Route and process telemetry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics&lt;/td&gt;
&lt;td&gt;Prometheus + Grafana&lt;/td&gt;
&lt;td&gt;Metric storage, dashboards, alerting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traces&lt;/td&gt;
&lt;td&gt;Grafana Tempo&lt;/td&gt;
&lt;td&gt;Distributed trace storage and search&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logs&lt;/td&gt;
&lt;td&gt;Grafana Loki&lt;/td&gt;
&lt;td&gt;Log aggregation with trace correlation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alerting&lt;/td&gt;
&lt;td&gt;Grafana Alerting&lt;/td&gt;
&lt;td&gt;Threshold-based alerts to Slack/PagerDuty&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This entire stack is open source and can run on a single server for small deployments, or scale horizontally as your traffic grows. A &lt;a href="https://www.deployhq.com/blog/what-is-docker" rel="noopener noreferrer"&gt;Docker Compose setup&lt;/a&gt; can have the full stack running in minutes.&lt;/p&gt;

&lt;p&gt;For teams that prefer managed solutions, OpenTelemetry exports to Datadog, New Relic, Honeycomb, Grafana Cloud, and AWS CloudWatch without any instrumentation changes — switch exporters in the collector config and you're done.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to Alert On
&lt;/h2&gt;

&lt;p&gt;The most common mistake with observability is alerting on symptoms instead of causes. &lt;q&gt;CPU at 90%&lt;/q&gt; is a symptom. &lt;q&gt;Database connection pool exhausted&lt;/q&gt; is a cause. Alert on the metrics closest to the actual problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good alerts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Error rate exceeds 1% of requests (5-minute rolling window)&lt;/li&gt;
&lt;li&gt;P99 response time exceeds 2 seconds&lt;/li&gt;
&lt;li&gt;Database connection pool active connections equals max pool size&lt;/li&gt;
&lt;li&gt;Queue depth exceeding consumer throughput for &amp;gt;5 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bad alerts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU above 80% (normal for many workloads)&lt;/li&gt;
&lt;li&gt;Any single 500 error (noise — use error rate instead)&lt;/li&gt;
&lt;li&gt;Disk usage above 70% (too early, creates alert fatigue)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Essential OpenTelemetry metrics to track:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http.server.request.duration&lt;/code&gt; — response time histogram, broken down by route and status code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;http.server.active_requests&lt;/code&gt; — current in-flight requests (early saturation warning)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db.client.connections.usage&lt;/code&gt; — database pool utilization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;process.runtime.*.memory&lt;/code&gt; — language-specific memory metrics (heap, RSS)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Logging too much.&lt;/strong&gt; Debug-level logging in production generates noise that obscures real signals. Use structured logging (JSON) with severity levels, and keep production at INFO or WARN. Use trace context to find detailed logs when you need them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ignoring trace propagation.&lt;/strong&gt; OpenTelemetry propagates trace context automatically for supported libraries, but custom HTTP clients, manual thread pools, and async operations can break the chain. If your traces end at service boundaries, check that W3C Trace Context headers (&lt;code&gt;traceparent&lt;/code&gt;, &lt;code&gt;tracestate&lt;/code&gt;) are being forwarded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sampling too aggressively too early.&lt;/strong&gt; Start with 100% sampling in development and staging. Only reduce sampling in production when storage costs justify it — and always keep 100% sampling for errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treating observability as optional.&lt;/strong&gt; &lt;q&gt;We'll add monitoring later&lt;/q&gt; is technical debt that compounds. Instrument from day one, even if it's just the auto-instrumentation agent. The cost of adding it is trivial compared to the cost of debugging a production incident blind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not connecting deployments to metrics.&lt;/strong&gt; Every deployment should be annotated in your monitoring dashboards. Without this correlation, the first 10 minutes of every incident are spent asking &lt;q&gt;did anything change?&lt;/q&gt; — a question your &lt;a href="https://dev.to/deployhq/building-a-cicd-pipeline-from-scratch-with-deployhq-a-step-by-step-guide-29lp"&gt;CI/CD pipeline&lt;/a&gt; already knows the answer to.&lt;/p&gt;




&lt;p&gt;Ready to deploy your observable application? &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles the &lt;a href="https://www.deployhq.com/blog/build-pipelines-in-deployhq-streamline-your-deployment-workflow" rel="noopener noreferrer"&gt;build and deployment pipeline&lt;/a&gt; while you focus on application quality. Post-deployment hooks make it easy to connect your releases to your monitoring — so you always know what changed and when.&lt;/p&gt;

&lt;p&gt;Questions about setting up observability for your services? Reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter/X&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>infrastructure</category>
      <category>tipstricks</category>
      <category>opentelemetry</category>
    </item>
    <item>
      <title>The Rise of Conversational Deployments: Will We Deploy with Natural Language?</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Mon, 23 Feb 2026 08:11:46 +0000</pubDate>
      <link>https://dev.to/deployhq/the-rise-of-conversational-deployments-will-we-deploy-with-natural-language-a80</link>
      <guid>https://dev.to/deployhq/the-rise-of-conversational-deployments-will-we-deploy-with-natural-language-a80</guid>
      <description>&lt;h1&gt;
  
  
  The Rise of Conversational Deployments: Will We Deploy with Natural Language?
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Exploring the future where &lt;q&gt;deploy to production&lt;/q&gt; is a conversation, not a checklist.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Picture this: You've just finished a feature. Instead of navigating to your deployment dashboard, selecting the right project, choosing the branch, configuring options, and clicking deploy, you simply say:&lt;/p&gt;

&lt;p&gt;&lt;q&gt;Deploy the user-authentication branch to staging, run the smoke tests, and let me know when it's ready.&lt;/q&gt;&lt;/p&gt;

&lt;p&gt;And it just happens.&lt;/p&gt;

&lt;p&gt;This isn't a far-off fantasy—it's the direction deployment tooling is heading. As we explored in &lt;a href="https://dev.to/deployhq/how-ai-coding-assistants-are-changing-the-way-we-deploy-code-1iic"&gt;How AI Coding Assistants Are Changing Deployments&lt;/a&gt;, the boundary between writing code and shipping code is blurring. In this post, we'll dive deeper into conversational deployments specifically—what's making them possible, and what this means for how developers will ship code in the near future.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Buttons to Commands to Conversation
&lt;/h2&gt;

&lt;p&gt;The history of deployment interfaces follows a clear progression toward simplicity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timeline
    title Evolution of Deployment Interfaces
    2000s : Manual FTP uploads
          : SSH and rsync scripts
    2010s : Web dashboards
          : Click-to-deploy buttons
    Mid-2010s : CLI tools
          : Git-based workflows
    2020s : ChatOps
          : Slack/Discord commands
    2025+ : Natural language
          : AI-powered conversation

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

&lt;/div&gt;



&lt;p&gt;Each evolution removed friction and made deployments more accessible. FTP required knowing server paths. CLIs required memorising commands. Dashboards simplified this to clicks—but you still needed to know where to click.&lt;/p&gt;

&lt;p&gt;Conversational deployments represent the next logical step: you describe what you want in plain language, and the system figures out how to make it happen.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Already want simpler deployments?&lt;/strong&gt; &lt;a href="https://www.deployhq.com/features/automatic-deployments" rel="noopener noreferrer"&gt;DeployHQ's automatic deployments&lt;/a&gt; trigger when you push to Git—no clicking required. It's the foundation for conversational deployment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Makes Conversational Deployments Different
&lt;/h2&gt;

&lt;p&gt;Conversational deployment isn't just voice-activated button clicking. It's a fundamentally different interaction model:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional Deployment&lt;/th&gt;
&lt;th&gt;Conversational Deployment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Navigate to dashboard&lt;/td&gt;
&lt;td&gt;State your intent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find the right project&lt;/td&gt;
&lt;td&gt;AI identifies context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Select options from menus&lt;/td&gt;
&lt;td&gt;AI infers sensible defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Click deploy&lt;/td&gt;
&lt;td&gt;AI executes and monitors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Watch progress manually&lt;/td&gt;
&lt;td&gt;AI reports status proactively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Check logs if something fails&lt;/td&gt;
&lt;td&gt;AI explains issues in plain language&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The key difference is &lt;strong&gt;intent over instruction&lt;/strong&gt;. You express what you want to achieve, not how to achieve it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example Conversations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Simple deployment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer: "Deploy main to production"

AI: "I'll deploy the main branch to production. This includes 
12 commits since the last deployment. The most recent commit 
is 'Add rate limiting to API endpoints' from 2 hours ago.

Starting deployment...
✓ Build completed (1m 23s)
✓ Tests passed (45s)
✓ Deployed to production

Production is now running commit abc123. All health checks 
are passing. Would you like me to monitor for any issues 
over the next 30 minutes?"

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Conditional deployment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer: "Deploy to staging, but only if all tests pass and 
there are no security vulnerabilities"

AI: "I'll deploy to staging with your conditions. Let me check...

✓ All 847 tests passing
✓ Security scan: 0 critical, 0 high vulnerabilities
  (2 low-severity items flagged for review later)

Conditions met. Proceeding with deployment...
✓ Deployed to staging.yourapp.com

I found 2 low-severity security items. Would you like me to 
summarise them?"

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

&lt;/div&gt;



&lt;p&gt;This kind of pre-deployment analysis is exactly what modern &lt;a href="https://dev.to/deployhq/ai-powered-deployment-troubleshooting-from-error-logs-to-fixes-in-seconds-2367-temp-slug-3139274"&gt;AI-powered deployment troubleshooting&lt;/a&gt; tools are designed for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Investigative deployment:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer: "What would happen if I deployed right now?"

AI: "If you deployed the current develop branch to staging:

**Changes:** 8 commits, +1,247 / -423 lines
**Risk assessment:** Medium
- Database migration adds new index (may take ~30s on production)
- New environment variable required: STRIPE_WEBHOOK_SECRET
- Dependency update: lodash 4.17.19 → 4.17.21 (security patch)

**Missing:** The STRIPE_WEBHOOK_SECRET isn't set in staging yet.

Would you like me to proceed anyway, or shall we add the 
environment variable first?"

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Technology Making This Possible
&lt;/h2&gt;

&lt;p&gt;Several technological advances are converging to enable conversational deployments:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Large Language Models (LLMs)
&lt;/h3&gt;

&lt;p&gt;Modern AI can understand nuanced natural language requests and translate them into specific actions. They handle ambiguity, ask clarifying questions when needed, and explain their actions in human terms.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Model Context Protocol (MCP)
&lt;/h3&gt;

&lt;p&gt;MCP provides a standardised way for AI assistants to interact with external tools. A deployment platform that implements MCP can expose its capabilities—triggering deployments, reading logs, managing environment variables—in a way any compatible AI assistant can use.&lt;/p&gt;

&lt;p&gt;For a technical deep dive, see &lt;a href="https://blog.deployhq.com/blog/mcp-future-of-ai-devops" rel="noopener noreferrer"&gt;MCP and the Future of AI-Integrated DevOps Tools&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
    subgraph "MCP-Enabled Deployment"
        User[Developer] --&amp;gt; AI[AI Assistant]

        AI &amp;lt;--&amp;gt; MCP[MCP Protocol]

        MCP &amp;lt;--&amp;gt; Deploy[DeployHQ API]
        MCP &amp;lt;--&amp;gt; Git[GitHub/GitLab]
        MCP &amp;lt;--&amp;gt; Monitor[Monitoring]

        Deploy --&amp;gt; Server[Servers]
    end

    style User fill:#64748B,color:#fff
    style AI fill:#0891B2,color:#fff
    style MCP fill:#0891B2,color:#fff
    style Deploy fill:#10B981,color:#fff
    style Git fill:#64748B,color:#fff
    style Monitor fill:#64748B,color:#fff
    style Server fill:#10B981,color:#fff

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Contextual Awareness
&lt;/h3&gt;

&lt;p&gt;AI assistants can now understand context across your entire development environment—your codebase, recent commits, deployment history, and current server state. This enables intelligent defaults and proactive warnings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing for Conversational Deployment
&lt;/h2&gt;

&lt;p&gt;If you're building or choosing deployment tools, here's what makes conversational interfaces effective:&lt;/p&gt;

&lt;h3&gt;
  
  
  Clear Mental Models
&lt;/h3&gt;

&lt;p&gt;Users need to understand what the AI can and cannot do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer: "Deploy to production and also update our DNS 
to point to the new server"

AI: "I can deploy to production, but I don't have access 
to your DNS settings. I'll proceed with the deployment, and 
you'll need to update DNS separately in your domain registrar.

Would you like me to continue with just the deployment?"

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Appropriate Guardrails
&lt;/h3&gt;

&lt;p&gt;Conversational doesn't mean careless. Production deployments should still require confirmation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer: "Deploy to production"

AI: "You're about to deploy to production. This will affect 
your live users.

**Summary:**
- Branch: main (commit abc123)
- Changes: 5 commits since last deploy
- Last deploy: 3 days ago

Type 'confirm' or click [Deploy Now] to proceed."

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;DeployHQ already supports guardrails:&lt;/strong&gt; Configure &lt;a href="https://www.deployhq.com/features" rel="noopener noreferrer"&gt;deployment approvals&lt;/a&gt; to require manual confirmation for production deployments, even when staging deploys automatically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Graceful Handling of Ambiguity
&lt;/h3&gt;

&lt;p&gt;Good conversational systems ask clarifying questions rather than guessing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer: "Deploy the latest changes"

AI: "I see recent changes on multiple branches:
- main: 2 commits (bug fixes)
- develop: 8 commits (new features)
- feature/api-v2: 15 commits (API redesign)

Which branch would you like to deploy, and to which environment?"

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Practical Implementation: A Prompt Library
&lt;/h2&gt;

&lt;p&gt;While full conversational deployment is still emerging, you can approximate it today using AI assistants with well-crafted prompts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deployment Request Prompt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a deployment assistant for a web application. 
The user will make deployment requests in natural language.

Available actions:
- Deploy [branch] to [environment]
- Rollback [environment] to [version]
- Check status of [environment]
- Compare [environment1] and [environment2]

Current project context:
- Environments: staging, production
- Default branch: main
- Deployment tool: DeployHQ

When the user makes a request:
1. Confirm you understand the request
2. Identify any risks or missing information
3. Ask for confirmation before destructive actions
4. Execute and report results

User request: [USER_INPUT]

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Status Check Prompt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Analyse the following deployment status and provide a 
human-readable summary:

- Include what was deployed and when
- Highlight any warnings or errors
- Suggest next steps if applicable

Status data:
[DEPLOYMENT_STATUS_JSON]

Respond conversationally, as if explaining to a colleague.

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Challenges Ahead
&lt;/h2&gt;

&lt;p&gt;Conversational deployments aren't without challenges:&lt;/p&gt;

&lt;h3&gt;
  
  
  Trust and Verification
&lt;/h3&gt;

&lt;p&gt;When deployments happen through conversation, how do you maintain an audit trail?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional&lt;/th&gt;
&lt;th&gt;Conversational&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UI shows who clicked deploy&lt;/td&gt;
&lt;td&gt;AI logs intent + actions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Settings visible in dashboard&lt;/td&gt;
&lt;td&gt;AI explains inferred settings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Manual review of each option&lt;/td&gt;
&lt;td&gt;AI requests confirmation for risks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;DeployHQ's &lt;a href="https://www.deployhq.com/features" rel="noopener noreferrer"&gt;deployment history&lt;/a&gt; already captures who triggered each deployment and what changed—essential groundwork for conversational audit trails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Edge Cases
&lt;/h3&gt;

&lt;p&gt;Natural language is ambiguous. &lt;q&gt;Deploy the latest&lt;/q&gt; could mean different things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latest commit on the current branch?&lt;/li&gt;
&lt;li&gt;Latest tag?&lt;/li&gt;
&lt;li&gt;Latest successful build?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good conversational systems develop conventions and ask when unclear.&lt;/p&gt;

&lt;h3&gt;
  
  
  Maintaining Control
&lt;/h3&gt;

&lt;p&gt;There's a balance between convenience and control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Full Manual Control] --&amp;gt; B[Guided Automation]
    B --&amp;gt; C[Conversational with Guardrails]
    C --&amp;gt; D[Full Autonomy]

    style A fill:#64748B,color:#fff
    style B fill:#64748B,color:#fff
    style C fill:#10B981,color:#fff
    style D fill:#F59E0B,color:#fff

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

&lt;/div&gt;



&lt;p&gt;The sweet spot is &lt;strong&gt;conversational with guardrails&lt;/strong&gt; —natural language for intent, confirmation for critical actions, and clear explanations throughout.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Developers
&lt;/h2&gt;

&lt;p&gt;Conversational deployments will be particularly transformative for:&lt;/p&gt;

&lt;h3&gt;
  
  
  Solo Developers and Freelancers
&lt;/h3&gt;

&lt;p&gt;No more remembering which button to click for which client's project. Just describe what you need: &lt;q&gt;Deploy the Johnson website to their staging server.&lt;/q&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Managing multiple client projects?&lt;/strong&gt; &lt;a href="https://www.deployhq.com/pricing" rel="noopener noreferrer"&gt;DeployHQ's team features&lt;/a&gt; let you organise projects by client and set different permissions for each.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Teams Without Dedicated DevOps
&lt;/h3&gt;

&lt;p&gt;Natural language lowers the barrier to deployment. Junior developers can deploy safely because the AI guides them through risks and confirms before proceeding.&lt;/p&gt;

&lt;p&gt;When something does go wrong, &lt;a href="https://dev.to/deployhq/ai-powered-deployment-troubleshooting-from-error-logs-to-fixes-in-seconds-2367-temp-slug-3139274"&gt;AI-powered troubleshooting&lt;/a&gt; helps them fix it without escalating to senior team members.&lt;/p&gt;

&lt;h3&gt;
  
  
  High-Velocity Teams
&lt;/h3&gt;

&lt;p&gt;When deployment is as easy as typing a message, shipping becomes faster. Teams can iterate more quickly without context-switching between development and operations tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing for the Conversational Future
&lt;/h2&gt;

&lt;p&gt;Even before full conversational deployment arrives, you can prepare:&lt;/p&gt;

&lt;h3&gt;
  
  
  Document Your Deployment Conventions
&lt;/h3&gt;

&lt;p&gt;Create a reference that an AI could use to understand your setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;deployment_conventions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;environments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;staging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;purpose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Testing&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;before&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;production"&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;develop&lt;/span&gt;
      &lt;span class="na"&gt;auto_deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;purpose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Live&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;user-facing"&lt;/span&gt;
      &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
      &lt;span class="na"&gt;requires_approval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;terminology&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latest"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main branch HEAD&lt;/span&gt;
    &lt;span class="s"&gt;"stable"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;most recent tag&lt;/span&gt;
    &lt;span class="s"&gt;"hotfix"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;branches matching hotfix/*&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Standardise Your Naming
&lt;/h3&gt;

&lt;p&gt;Consistent naming helps AI understand intent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;feature/*&lt;/code&gt; branches → staging deployment candidates&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hotfix/*&lt;/code&gt; branches → production deployment candidates&lt;/li&gt;
&lt;li&gt;Clear environment names: &lt;code&gt;staging&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;, not &lt;code&gt;server2&lt;/code&gt;, &lt;code&gt;live-new&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build Confidence in Automation
&lt;/h3&gt;

&lt;p&gt;If you don't trust automated deployments now, you won't trust conversational ones. Start by automating your staging deployments with &lt;a href="https://www.deployhq.com/features/automatic-deployments" rel="noopener noreferrer"&gt;DeployHQ's automatic deployments&lt;/a&gt;, then gradually extend automation to production with appropriate guardrails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Conversational deployments represent the next evolution in how we ship code—from manual processes to clicks to commands to natural language. Here's what to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conversational deployment is about expressing intent, not following procedures&lt;/li&gt;
&lt;li&gt;Technologies like LLMs and MCP are making this possible today&lt;/li&gt;
&lt;li&gt;Good conversational systems include guardrails for critical actions&lt;/li&gt;
&lt;li&gt;You can prepare now by documenting conventions and standardising naming&lt;/li&gt;
&lt;li&gt;The goal is reduced friction without reduced control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The future of deployment is a conversation. And that conversation is starting now.&lt;/p&gt;




&lt;h2&gt;
  
  
  Continue Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/deployhq/how-ai-coding-assistants-are-changing-the-way-we-deploy-code-1iic"&gt;How AI Coding Assistants Are Changing Deployments&lt;/a&gt; — The broader transformation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/deployhq/ai-powered-deployment-troubleshooting-from-error-logs-to-fixes-in-seconds-2367-temp-slug-3139274"&gt;AI-Powered Deployment Troubleshooting&lt;/a&gt; — When things go wrong&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/deployhq/mcp-and-the-future-of-ai-integrated-devops-tools-29fk"&gt;MCP and the Future of AI-Integrated DevOps&lt;/a&gt; — The technical foundation&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Ready to start your journey toward simpler deployments?&lt;/strong&gt; &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Start your free&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; trial and set up automatic deployments in minutes. It's the first step toward conversational deployment.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Have ideas for AI deployment features? &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Tell us on X&lt;/a&gt; — we'd love to hear what you'd build.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devopsinfrastructure</category>
      <category>news</category>
      <category>naturallanguage</category>
    </item>
    <item>
      <title>Turbo Deployments Explained: Deploy Faster with One Simple Trick</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Wed, 18 Feb 2026 08:41:46 +0000</pubDate>
      <link>https://dev.to/deployhq/turbo-deployments-explained-deploy-faster-with-one-simple-trick-3olo</link>
      <guid>https://dev.to/deployhq/turbo-deployments-explained-deploy-faster-with-one-simple-trick-3olo</guid>
      <description>&lt;p&gt;If you've ever watched a deployment progress bar crawl across your screen, uploading file after file after file, you know the frustration. Traditional file-by-file deployments are one of the biggest time sinks in modern development workflows -- especially for projects with hundreds or thousands of files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turbo Deployments&lt;/strong&gt; change the game entirely. Instead of transferring each file individually, Turbo bundles everything into a single compressed package, sends it in one transfer, and unpacks it on the server at local disk speed. The result? Deployments that are &lt;strong&gt;up to 10x faster&lt;/strong&gt; than traditional methods.&lt;/p&gt;

&lt;p&gt;In this guide, we'll cover how traditional deployments work (and why they're slow), how Turbo Deployments solve the problem, what you need on your server, when to use them, and how to enable the feature in &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;. By the end, you'll have a clear understanding of whether Turbo is right for your workflow -- and how to start using it today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Old Way: Uploading Files One-by-One
&lt;/h2&gt;

&lt;p&gt;Traditional deployments work like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your Computer Your Server
|-- index.php -&amp;gt; |-- index.php (2 seconds)
|-- style.css -&amp;gt; |-- style.css (2 seconds)
|-- app.js -&amp;gt; |-- app.js (2 seconds)
|-- config.php -&amp;gt; |-- config.php (2 seconds)
+-- ... 500 more files +-- ... (1000 seconds!)

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

&lt;/div&gt;



&lt;p&gt;Each file makes its own trip across the internet. With 500 files, that's 500 separate connections. Every connection involves a handshake, authentication, file transfer, and confirmation -- all of which adds overhead. That connecting and disconnecting adds up to a LOT of wasted time, especially over high-latency connections or when deploying to servers in distant regions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like:&lt;/strong&gt; Carrying groceries from your car to your kitchen one item at a time. Sure, it works, but it's painfully slow!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Turbo Way: Bundle, Send, Unpack
&lt;/h2&gt;

&lt;p&gt;Turbo Deployments take a fundamentally different approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Your Files&amp;lt;br/&amp;gt;1000 files] --&amp;gt; B[Bundle&amp;lt;br/&amp;gt;.tar.gz]
    B --&amp;gt; C[Single Transfer&amp;lt;br/&amp;gt;to Server]
    C --&amp;gt; D[Unpack Locally&amp;lt;br/&amp;gt;on Server]
    D --&amp;gt; E[All Files&amp;lt;br/&amp;gt;in Place]

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

&lt;/div&gt;



&lt;p&gt;Instead of making hundreds of individual transfers, the entire deployment is compressed into a single &lt;code&gt;.tar.gz&lt;/code&gt; archive, transferred once, and then extracted directly on the server. Since the extraction happens locally on the server's filesystem, it runs at disk speed rather than network speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time saved:&lt;/strong&gt; Instead of 1000 seconds, maybe 50 seconds total!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like:&lt;/strong&gt; Putting all your groceries in bags, making ONE trip, then unpacking inside your kitchen.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works Step by Step
&lt;/h2&gt;

&lt;p&gt;Here's what happens behind the scenes when you trigger a Turbo Deployment:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Bundle Everything
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; identifies all the files that need to be deployed (based on your Git diff or full deployment), then compresses them into a single &lt;code&gt;.tar.gz&lt;/code&gt; archive. This is the same compression format used by Linux system administrators worldwide -- it's battle-tested and efficient.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before deployment:
|-- app/
| |-- models/
| |-- views/
| +-- controllers/
|-- public/
| |-- css/
| +-- js/
+-- config/

(compress into one file)

deployment.tar.gz (one compressed file)

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

&lt;/div&gt;



&lt;p&gt;Text-based source code compresses extremely well. A project with 50MB of source files might compress down to 5-10MB, making the transfer even faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Send One File
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your Computer ============&amp;gt; Your Server
    deployment.tar.gz
     (one fast transfer!)

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

&lt;/div&gt;



&lt;p&gt;Instead of 500 separate uploads with 500 connection handshakes, there's just ONE upload over a single SSH connection. This eliminates the per-file overhead that makes traditional deployments so slow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Server Unpacks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;On your server:

deployment.tar.gz

(extract)

|-- app/
| |-- models/ [done]
| |-- views/ [done]
| +-- controllers/[done]
|-- public/
| |-- css/ [done]
| +-- js/ [done]
+-- config/ [done]

(all organized and ready!)

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

&lt;/div&gt;



&lt;p&gt;Your server extracts everything and puts files in the right places using &lt;code&gt;rsync&lt;/code&gt;. Since this happens entirely ON the server, it's lightning fast -- no internet latency involved. The server's local disk I/O handles the heavy lifting, and modern SSDs can extract thousands of files in seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Is This So Much Faster?
&lt;/h2&gt;

&lt;p&gt;Three main reasons:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. One Transfer Instead of Many
&lt;/h3&gt;

&lt;p&gt;Every file transfer involves overhead: DNS resolution, TCP handshake, SSH authentication, channel setup, and confirmation. With traditional deployments, you pay this cost for every single file.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traditional:&lt;/strong&gt; 500 files = 500 separate connections = 500x overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turbo:&lt;/strong&gt; 500 files = 1 connection = 1x overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Compression Makes It Smaller
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;.tar.gz&lt;/code&gt; package is compressed, so it's significantly smaller to transfer. Source code -- HTML, CSS, JavaScript, PHP, Python -- is highly compressible text. You'll typically see 70-90% compression ratios, meaning you're transferring a fraction of the original data.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Local Speed vs. Internet Speed
&lt;/h3&gt;

&lt;p&gt;Once the package is on your server, unpacking happens at local disk speed (hundreds of MB/s on SSDs) instead of internet speed (typically 1-100 MB/s). This difference alone can account for a 10x improvement on the extraction phase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world benchmarks:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Traditional&lt;/th&gt;
&lt;th&gt;Turbo&lt;/th&gt;
&lt;th&gt;Speedup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;500 files, shared hosting&lt;/td&gt;
&lt;td&gt;~15 min&lt;/td&gt;
&lt;td&gt;~2 min&lt;/td&gt;
&lt;td&gt;7.5x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000 files, VPS&lt;/td&gt;
&lt;td&gt;~20 min&lt;/td&gt;
&lt;td&gt;~3 min&lt;/td&gt;
&lt;td&gt;6.7x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2,000+ files, dedicated&lt;/td&gt;
&lt;td&gt;~40 min&lt;/td&gt;
&lt;td&gt;~4 min&lt;/td&gt;
&lt;td&gt;10x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What You Need to Use Turbo Deployments
&lt;/h2&gt;

&lt;p&gt;Your server needs a few standard tools installed. The good news is that most Linux servers already have these out of the box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://deployhq.com/blog/openssh-on-windows-why-this-changes-everything-for-your-deployments" rel="noopener noreferrer"&gt;SSH access&lt;/a&gt; (not just FTP)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tar&lt;/code&gt; (for unpacking -- included in virtually all Linux distributions)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gzip&lt;/code&gt; (for decompression -- also standard on Linux)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deployhq.com/blog/sftp-vs-scp-vs-rsync-choosing-the-right-file-transfer-method" rel="noopener noreferrer"&gt;&lt;code&gt;rsync&lt;/code&gt;&lt;/a&gt; (for organizing files into the correct locations)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;jq&lt;/code&gt; (for reading deployment instructions -- easily installed via package manager)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Turbo Deployments won't work with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FTP-only servers (no SSH access)&lt;/li&gt;
&lt;li&gt;SFTP-only connections (without full SSH shell access)&lt;/li&gt;
&lt;li&gt;Cloud storage targets (like Amazon S3 or Google Cloud Storage)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it works great with any regular Linux/Unix server where you have SSH access -- which covers the vast majority of web hosting environments, VPS providers, and dedicated servers.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Should You Use Turbo Deployments?
&lt;/h2&gt;

&lt;p&gt;Turbo Deployments are perfect when you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; projects with lots of files (100+)&lt;/li&gt;
&lt;li&gt;Notice your current deployments feel slow&lt;/li&gt;
&lt;li&gt;Have SSH access to your server&lt;/li&gt;
&lt;li&gt;Want to reduce deployment downtime for your users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They're especially impactful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://deployhq.com/blog/case-study-digital-agency-achieves-zero-downtime-deployments-on-wp-engine-with-deployhq" rel="noopener noreferrer"&gt;WordPress sites&lt;/a&gt; with lots of plugins and themes&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deployhq.com/blog/5-powerful-ways-to-deploy-php-applications-with-deployhq" rel="noopener noreferrer"&gt;Laravel/Symfony PHP apps&lt;/a&gt; with large vendor directories&lt;/li&gt;
&lt;li&gt;React/Vue/Angular front-end builds with bundled assets&lt;/li&gt;
&lt;li&gt;Node.js applications with extensive &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Monorepos and multi-service projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thousands of teams already use &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; to ship code faster. Turbo Deployments are one of the most impactful features for teams deploying medium-to-large projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Enable It
&lt;/h2&gt;

&lt;p&gt;In DeployHQ:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to your &lt;a href="https://deployhq.com/features/deployment-targets" rel="noopener noreferrer"&gt;server settings&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Check if your server is compatible (DeployHQ will automatically detect this)&lt;/li&gt;
&lt;li&gt;Enable &lt;q&gt;Use Turbo Deployments&lt;/q&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; as normal -- everything else stays the same!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; automatically checks if your server has the right tools installed. If something's missing, it'll gracefully fall back to regular deployments, so there's no risk in enabling it.&lt;/p&gt;

&lt;p&gt;Turbo Deployments are available on all &lt;a href="https://www.deployhq.com/pricing" rel="noopener noreferrer"&gt;DeployHQ plans&lt;/a&gt;, including the free tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;If Turbo Deployments aren't working as expected, here are the most common issues:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;q&gt;Server not compatible&lt;/q&gt; message:&lt;/strong&gt; Your server is likely missing one of the required tools (&lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;gzip&lt;/code&gt;, &lt;code&gt;rsync&lt;/code&gt;, or &lt;code&gt;jq&lt;/code&gt;). SSH into your server and install the missing package. On Ubuntu/Debian: &lt;code&gt;sudo apt-get install rsync jq&lt;/code&gt;. On CentOS/RHEL: &lt;code&gt;sudo yum install rsync jq&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployments falling back to traditional mode:&lt;/strong&gt; &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; runs a compatibility check before each Turbo deployment. Check your server logs or DeployHQ's deployment log for details on what failed the check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permission errors during extraction:&lt;/strong&gt; The SSH user must have write permissions to the deployment directory. Ensure your deployment path is writable by the user specified in your server configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can I use Turbo with zero-downtime deployments?&lt;/strong&gt; Yes! Turbo Deployments work alongside DeployHQ's &lt;a href="https://deployhq.com/features/zero-downtime-deployments" rel="noopener noreferrer"&gt;zero-downtime deployment&lt;/a&gt; feature. The compressed transfer happens first, then the atomic symlink switch ensures no downtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will Turbo work with my build pipeline?&lt;/strong&gt; Absolutely. Turbo Deployments handle the file transfer step. Your &lt;a href="https://deployhq.com/support/build-pipelines" rel="noopener noreferrer"&gt;build commands&lt;/a&gt; run before the transfer, so compiled assets, minified files, and generated code all get bundled into the archive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if my server doesn't support Turbo?&lt;/strong&gt; &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; automatically falls back to traditional file-by-file deployment. You won't experience any errors -- just the standard deployment speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there a file size limit?&lt;/strong&gt; There's no hard limit on the archive size. However, very large projects (10GB+) may benefit from using DeployHQ's &lt;a href="https://deployhq.com/support/config-files" rel="noopener noreferrer"&gt;config file exclusions&lt;/a&gt; to skip unnecessary files like test fixtures or documentation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does Turbo work with atomic deployments?&lt;/strong&gt; Yes. Turbo handles the transfer; atomicity is managed by the deployment strategy. They complement each other perfectly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Turbo Deployments are like using a moving truck instead of carrying boxes in your car. Same destination, way fewer trips, and you're done in a fraction of the time.&lt;/p&gt;

&lt;p&gt;If you're deploying projects with more than a hundred files and have SSH access to your server, Turbo Deployments can save you serious time on every single deployment. For teams deploying multiple times a day, those minutes add up to hours saved every week.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Ready to speed up your deployments?&lt;/strong&gt; &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Sign up for DeployHQ&lt;/a&gt; and enable Turbo Deployments today, or check out our &lt;a href="https://www.deployhq.com/pricing" rel="noopener noreferrer"&gt;pricing plans&lt;/a&gt; to find the right fit for your team. Questions? Reach out to us at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;Twitter @deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>infrastructure</category>
      <category>tutorials</category>
      <category>turbodeployments</category>
    </item>
    <item>
      <title>Serverless Deployments with DeployHQ: AWS Lambda, Vercel, and Netlify</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Mon, 16 Feb 2026 06:30:54 +0000</pubDate>
      <link>https://dev.to/deployhq/serverless-deployments-with-deployhq-aws-lambda-vercel-and-netlify-8ep</link>
      <guid>https://dev.to/deployhq/serverless-deployments-with-deployhq-aws-lambda-vercel-and-netlify-8ep</guid>
      <description>&lt;p&gt;Serverless computing has revolutionized how we deploy applications, eliminating server management while enabling automatic scaling. Whether you're deploying to AWS Lambda, Vercel, or Netlify, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; can streamline your serverless deployment workflow. In this guide, you'll learn how to integrate serverless platforms with your existing deployment pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Serverless Deployments
&lt;/h2&gt;

&lt;p&gt;Serverless doesn't mean &lt;q&gt;no servers&lt;/q&gt;—it means you don't manage them. The platform handles infrastructure, scaling, and availability. Your focus shifts entirely to code and business logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    subgraph "Traditional"
        T1[Code] --&amp;gt; T2[Build]
        T2 --&amp;gt; T3[Configure Server]
        T3 --&amp;gt; T4[Deploy]
        T4 --&amp;gt; T5[Scale Manually]
    end

    subgraph "Serverless"
        S1[Code] --&amp;gt; S2[Build]
        S2 --&amp;gt; S3[Deploy Function]
        S3 --&amp;gt; S4[Auto-Scale]
    end

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

&lt;/div&gt;



&lt;p&gt;If you're new to modern web architecture, check out our guide on &lt;a href="https://deployhq.com/blog/jamstack-vs-traditional-cms-when-to-choose-each-and-how-to-deploy-both" rel="noopener noreferrer"&gt;JAMStack vs. Traditional CMS&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Lambda Deployment with DeployHQ
&lt;/h2&gt;

&lt;p&gt;AWS Lambda is the most mature serverless platform. Here's how to deploy Lambda functions using DeployHQ's build pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;serverless-app/
├── src/
│ ├── handlers/
│ │ ├── api.js
│ │ ├── webhook.js
│ │ └── scheduler.js
│ └── lib/
│ └── database.js
├── package.json
├── serverless.yml
└── .deployhq/
    └── deploy.sh

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Serverless Framework Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# serverless.yml&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-serverless-api&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs20.x&lt;/span&gt;
  &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:AWS_REGION, 'us-east-1'}&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:DATABASE_URL}&lt;/span&gt;
    &lt;span class="na"&gt;API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${env:API_KEY}&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/handlers/api.handler&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/{proxy+}&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;any&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;memorySize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;512&lt;/span&gt;

  &lt;span class="na"&gt;webhook&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/handlers/webhook.handler&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/webhook&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;post&lt;/span&gt;

  &lt;span class="na"&gt;scheduler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/handlers/scheduler.handler&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rate(1 hour)&lt;/span&gt;

&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;serverless-offline&lt;/span&gt;

&lt;span class="na"&gt;package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/**&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.git/**&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tests/**&lt;/span&gt;
  &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;src/**&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  DeployHQ Build Commands
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
npm ci

&lt;span class="c"&gt;# Run tests&lt;/span&gt;
npm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Package and deploy with Serverless Framework&lt;/span&gt;
npx serverless deploy &lt;span class="nt"&gt;--stage&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEPLOY_ENVIRONMENT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;production&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Output deployed endpoints&lt;/span&gt;
npx serverless info &lt;span class="nt"&gt;--stage&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DEPLOY_ENVIRONMENT&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;production&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Environment Variables Setup
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; project settings, add these environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;AKIA...&lt;/span&gt;
&lt;span class="py"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;xxx&lt;/span&gt;
&lt;span class="py"&gt;AWS_REGION&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;
&lt;span class="py"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;postgresql://...&lt;/span&gt;
&lt;span class="py"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;xxx&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For secure credential management, see our guide on &lt;a href="https://deployhq.com/blog/protecting-your-api-keys-a-quick-guide" rel="noopener noreferrer"&gt;protecting your API keys&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vercel Deployment Integration
&lt;/h2&gt;

&lt;p&gt;Vercel excels at deploying Next.js applications and serverless functions. While Vercel has its own Git integration, you can also deploy via &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; for more control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Structure for Vercel
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nextjs-app/
├── pages/
│ ├── api/
│ │ ├── users.js
│ │ └── products.js
│ ├── index.js
│ └── about.js
├── components/
├── public/
├── package.json
├── vercel.json
└── next.config.js

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vercel Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;vercel.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"builds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"package.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"use"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@vercel/next"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/(.*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/$1"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"DATABASE_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@database_url"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"NEXT_PUBLIC_API_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@api_url"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  DeployHQ Build Commands for Vercel
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Vercel CLI&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; vercel

&lt;span class="c"&gt;# Install project dependencies&lt;/span&gt;
npm ci

&lt;span class="c"&gt;# Run tests&lt;/span&gt;
npm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Build Next.js&lt;/span&gt;
npm run build

&lt;span class="c"&gt;# Deploy to Vercel&lt;/span&gt;
vercel deploy &lt;span class="nt"&gt;--prod&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="nv"&gt;$VERCEL_TOKEN&lt;/span&gt;

&lt;span class="c"&gt;# Or deploy preview for non-main branches&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_BRANCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;vercel deploy &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="nv"&gt;$VERCEL_TOKEN&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;vercel deploy &lt;span class="nt"&gt;--prod&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="nv"&gt;$VERCEL_TOKEN&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Serverless API Route Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/users.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/database&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;take&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="na"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to fetch users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to create user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Allow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;405&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Method &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Not Allowed`&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 Next.js deployment specifics, see our guide on &lt;a href="https://deployhq.com/blog/deploying-a-next-js-application-on-hostinger-with-ubuntu-22-04-using-deployhq" rel="noopener noreferrer"&gt;deploying a Next.js application&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Netlify Functions Deployment
&lt;/h2&gt;

&lt;p&gt;Netlify offers a simpler serverless experience with automatic function deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;netlify-app/
├── netlify/
│ └── functions/
│ ├── hello.js
│ └── submit-form.js
├── src/
│ └── index.html
├── package.json
└── netlify.toml

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Netlify Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# netlify.toml&lt;/span&gt;
&lt;span class="nn"&gt;[build]&lt;/span&gt;
  &lt;span class="py"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"npm run build"&lt;/span&gt;
  &lt;span class="py"&gt;publish&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dist"&lt;/span&gt;
  &lt;span class="py"&gt;functions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"netlify/functions"&lt;/span&gt;

&lt;span class="nn"&gt;[build.environment]&lt;/span&gt;
  &lt;span class="py"&gt;NODE_VERSION&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"20"&lt;/span&gt;

&lt;span class="nn"&gt;[functions]&lt;/span&gt;
  &lt;span class="py"&gt;directory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"netlify/functions"&lt;/span&gt;

&lt;span class="nn"&gt;[[redirects]]&lt;/span&gt;
  &lt;span class="py"&gt;from&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/api/*"&lt;/span&gt;
  &lt;span class="py"&gt;to&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/.netlify/functions/:splat"&lt;/span&gt;
  &lt;span class="py"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Netlify Function Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// netlify/functions/submit-form.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@supabase/supabase-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SUPABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SUPABASE_KEY&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;httpMethod&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;405&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Method not allowed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Validate input&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Missing required fields&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Save to database&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submissions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Form submitted successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&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="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Internal server error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  DeployHQ Build Commands for Netlify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Netlify CLI&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; netlify-cli

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
npm ci

&lt;span class="c"&gt;# Build site&lt;/span&gt;
npm run build

&lt;span class="c"&gt;# Deploy to Netlify&lt;/span&gt;
netlify deploy &lt;span class="nt"&gt;--prod&lt;/span&gt; &lt;span class="nt"&gt;--auth&lt;/span&gt; &lt;span class="nv"&gt;$NETLIFY_AUTH_TOKEN&lt;/span&gt; &lt;span class="nt"&gt;--site&lt;/span&gt; &lt;span class="nv"&gt;$NETLIFY_SITE_ID&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Edge Functions: The Next Evolution
&lt;/h2&gt;

&lt;p&gt;Edge functions run closer to your users, reducing latency. Both Vercel and Netlify support them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TB
    subgraph "Traditional Lambda"
        U1[User in Tokyo] --&amp;gt; R1[us-east-1]
        R1 --&amp;gt; U1
    end

    subgraph "Edge Functions"
        U2[User in Tokyo] --&amp;gt; E1[Tokyo Edge]
        E1 --&amp;gt; U2
    end

    style E1 fill:#90EE90
    style R1 fill:#FFB6C1

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Vercel Edge Function Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/geo.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;edge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;geo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Netlify Edge Function Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// netlify/edge-functions/geo.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/geo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment Pipeline Flow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant Dev as Developer
    participant DHQ as DeployHQ
    participant Build as Build Server
    participant Platform as Serverless Platform

    Dev-&amp;gt;&amp;gt;DHQ: git push
    DHQ-&amp;gt;&amp;gt;Build: Trigger build
    Build-&amp;gt;&amp;gt;Build: npm install
    Build-&amp;gt;&amp;gt;Build: npm test
    Build-&amp;gt;&amp;gt;Build: npm run build
    Build-&amp;gt;&amp;gt;Platform: Deploy (CLI)
    Platform-&amp;gt;&amp;gt;Platform: Validate
    Platform-&amp;gt;&amp;gt;Platform: Deploy functions
    Platform-&amp;gt;&amp;gt;Build: Success + URLs
    Build-&amp;gt;&amp;gt;DHQ: Complete
    DHQ-&amp;gt;&amp;gt;Dev: Notification

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Environment Management
&lt;/h2&gt;

&lt;p&gt;Managing environments in serverless requires careful configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config/index.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environments&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:3000/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;databaseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEV_DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;staging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://staging-api.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;databaseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STAGING_DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;apiUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;databaseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROD_DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;environments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For more on environment management, see &lt;a href="https://deployhq.com/blog/managing-multiple-environments-with-deployhq-dev-staging-and-production" rel="noopener noreferrer"&gt;managing multiple environments with DeployHQ&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cold Starts and Performance
&lt;/h2&gt;

&lt;p&gt;Cold starts are the biggest challenge in serverless. Mitigate them with:&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep Functions Warm
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AWS Lambda warmer&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check if this is a warming call&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;serverless-plugin-warmup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Warming up!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Warmed!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Normal handler logic&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Optimize Bundle Size
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;lighter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;alternatives&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"date-fns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.30.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Instead&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;moment.js&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esbuild"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.19.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Fast&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;bundler&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connection Pooling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/database.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PrismaClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@prisma/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Reuse connection across invocations&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Prevent too many connections in development&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PrismaClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;global&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Monitoring and Debugging
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AWS CloudWatch Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Structured logging for CloudWatch&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;requestId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AWS_REQUEST_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request received&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Keep functions small&lt;/strong&gt; and focused on single tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimize cold starts&lt;/strong&gt; with proper initialization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use environment variables&lt;/strong&gt; for configuration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement proper error handling&lt;/strong&gt; with structured logging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test locally&lt;/strong&gt; before deploying (serverless-offline, netlify dev)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor performance&lt;/strong&gt; and set up alerts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use edge functions&lt;/strong&gt; for latency-sensitive operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bundle dependencies&lt;/strong&gt; efficiently&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Ready to go serverless? Here's your action plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose your platform (Lambda, Vercel, Netlify)&lt;/li&gt;
&lt;li&gt;Set up project structure&lt;/li&gt;
&lt;li&gt;Configure &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; build commands&lt;/li&gt;
&lt;li&gt;Add environment variables&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; and monitor&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For traditional deployments, see our &lt;a href="https://deployhq.com/blog/building-a-ci-cd-pipeline-from-scratch-with-deployhq-a-step-by-step-guide" rel="noopener noreferrer"&gt;CI/CD pipeline guide&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Questions about serverless deployments? Contact &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or follow &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;@deployhq&lt;/a&gt; for the latest guides.&lt;/p&gt;

</description>
      <category>awslambda</category>
      <category>vercel</category>
      <category>netlify</category>
      <category>node</category>
    </item>
    <item>
      <title>SFTP vs SCP vs rsync: Choosing the Right File Transfer Method</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Fri, 13 Feb 2026 06:45:03 +0000</pubDate>
      <link>https://dev.to/deployhq/sftp-vs-scp-vs-rsync-choosing-the-right-file-transfer-method-1ljh</link>
      <guid>https://dev.to/deployhq/sftp-vs-scp-vs-rsync-choosing-the-right-file-transfer-method-1ljh</guid>
      <description>&lt;p&gt;Every deployment ultimately comes down to transferring files from one machine to another. Whether you're pushing code to a server, syncing assets to a staging environment, or downloading backups, you need a secure file transfer method. The three most common options — SFTP, SCP, and rsync — all use SSH for encryption, but they work very differently under the hood.&lt;/p&gt;

&lt;p&gt;This guide compares all three with practical examples and helps you choose the right one for your &lt;a href="https://deployhq.com/features" rel="noopener noreferrer"&gt;deployment workflow&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;SFTP&lt;/th&gt;
&lt;th&gt;SCP&lt;/th&gt;
&lt;th&gt;rsync&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protocol&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;SSH File Transfer Protocol&lt;/td&gt;
&lt;td&gt;Secure Copy (over SSH)&lt;/td&gt;
&lt;td&gt;Remote sync (over SSH)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Transfer type&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full file transfer&lt;/td&gt;
&lt;td&gt;Full file transfer&lt;/td&gt;
&lt;td&gt;Delta transfer (only changes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resume interrupted transfers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Directory listing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Interactive mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes (like FTP)&lt;/td&gt;
&lt;td&gt;No (command-line only)&lt;/td&gt;
&lt;td&gt;No (command-line only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bandwidth efficiency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Excellent (delta algorithm)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Status in 2025&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Active, recommended&lt;/td&gt;
&lt;td&gt;Deprecated in OpenSSH 9.0&lt;/td&gt;
&lt;td&gt;Active, recommended&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;General file management&lt;/td&gt;
&lt;td&gt;Legacy one-off copies&lt;/td&gt;
&lt;td&gt;Incremental deployments&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  SFTP: SSH File Transfer Protocol
&lt;/h2&gt;

&lt;p&gt;SFTP is the modern standard for secure file transfer. Despite the name, it has nothing to do with FTP — it's a completely separate protocol that runs over SSH.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;SFTP establishes an SSH connection and then provides a file transfer subsystem on top of it. You get a full set of file operations: upload, download, rename, delete, list directories, change permissions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Examples
&lt;/h3&gt;

&lt;p&gt;Upload a file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;sftp user@server.example.com
&lt;/span&gt;&lt;span class="gp"&gt;sftp&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;put index.html /var/www/html/
&lt;span class="gp"&gt;sftp&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;span class="go"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Non-interactive upload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;sftp user@server.example.com &amp;lt;&amp;lt;EOF
put -r dist/ /var/www/html/
EOF

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

&lt;/div&gt;



&lt;p&gt;Upload with a specific SSH key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;sftp -i ~/.ssh/deploy_key user@server.example.com

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

&lt;/div&gt;



&lt;p&gt;Download a directory recursively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;sftp user@server.example.com
&lt;/span&gt;&lt;span class="gp"&gt;sftp&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;get &lt;span class="nt"&gt;-r&lt;/span&gt; /var/www/html/backups/ ./local-backups/
&lt;span class="go"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to Use SFTP
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;General-purpose file management on remote servers&lt;/li&gt;
&lt;li&gt;Interactive file browsing and operations&lt;/li&gt;
&lt;li&gt;When you need resume support for large transfers&lt;/li&gt;
&lt;li&gt;As a replacement for FTP (which is insecure)&lt;/li&gt;
&lt;li&gt;Automated deployments via tools like &lt;a href="https://deployhq.com/features" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SCP: Secure Copy Protocol
&lt;/h2&gt;

&lt;p&gt;SCP is the simplest file transfer method — it copies files over SSH with a single command. However, OpenSSH deprecated the SCP protocol in version 9.0 (released April 2022) in favor of SFTP.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Deprecation
&lt;/h3&gt;

&lt;p&gt;Modern &lt;code&gt;scp&lt;/code&gt; commands actually use SFTP internally by default (since OpenSSH 9.0). The &lt;code&gt;scp&lt;/code&gt; command still exists for backward compatibility, but the underlying protocol is now SFTP. You can force the legacy SCP protocol with &lt;code&gt;-O&lt;/code&gt;, but there's rarely a reason to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Examples
&lt;/h3&gt;

&lt;p&gt;Copy a file to a remote server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;scp index.html user@server.example.com:/var/www/html/

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

&lt;/div&gt;



&lt;p&gt;Copy a directory recursively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;scp -r dist/ user@server.example.com:/var/www/html/

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

&lt;/div&gt;



&lt;p&gt;Copy with a specific SSH key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;scp -i ~/.ssh/deploy_key -r dist/ user@server.example.com:/var/www/html/

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

&lt;/div&gt;



&lt;p&gt;Download from remote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;scp user@server.example.com:/var/www/html/backup.tar.gz ./

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to Use SCP
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Quick one-off file copies where you don't need interactivity&lt;/li&gt;
&lt;li&gt;Legacy scripts that already use &lt;code&gt;scp&lt;/code&gt; (they'll use SFTP under the hood now)&lt;/li&gt;
&lt;li&gt;Simple transfers where rsync's features aren't needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  rsync: The Delta Transfer Tool
&lt;/h2&gt;

&lt;p&gt;rsync is the most powerful of the three. Its killer feature is the delta transfer algorithm — it compares files on both sides and only transfers the differences. For deployments where most files haven't changed, this is dramatically faster than copying everything.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
  A[Local files] --&amp;gt; B[rsync compares checksums]
  C[Remote files] --&amp;gt; B
  B --&amp;gt; D[Transfer only changed bytes]
  D --&amp;gt; E[Updated remote files]

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

&lt;/div&gt;



&lt;p&gt;rsync splits each file into blocks, computes checksums, and sends only the blocks that differ. A 10 MB file with a one-line change might only transfer a few kilobytes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Examples
&lt;/h3&gt;

&lt;p&gt;Sync a directory to a remote server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;rsync -avz dist/ user@server.example.com:/var/www/html/

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

&lt;/div&gt;



&lt;p&gt;Flags explained:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-a&lt;/code&gt; (archive): preserves permissions, timestamps, symlinks, ownership&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v&lt;/code&gt; (verbose): shows progress&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-z&lt;/code&gt; (compress): compresses data during transfer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sync with delete (remove files on remote that don't exist locally):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;rsync -avz --delete dist/ user@server.example.com:/var/www/html/

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

&lt;/div&gt;



&lt;p&gt;Dry run (see what would change without actually transferring):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;rsync -avzn --delete dist/ user@server.example.com:/var/www/html/

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

&lt;/div&gt;



&lt;p&gt;Exclude files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;rsync -avz --exclude='node_modules' --exclude='.env' \
  ./ user@server.example.com:/var/www/html/

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

&lt;/div&gt;



&lt;p&gt;Use a specific SSH key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;rsync -avz -e "ssh -i ~/.ssh/deploy_key" \
  dist/ user@server.example.com:/var/www/html/

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to Use rsync
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deployments with large codebases where most files don't change&lt;/li&gt;
&lt;li&gt;Syncing directories that change incrementally&lt;/li&gt;
&lt;li&gt;Backups (only transfer what changed since last backup)&lt;/li&gt;
&lt;li&gt;Any transfer where bandwidth is limited or expensive&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance Comparison
&lt;/h2&gt;

&lt;p&gt;For a project with 1,000 files (50 MB total) where 10 files changed since the last deploy:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;First deploy&lt;/th&gt;
&lt;th&gt;Subsequent deploys&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SFTP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~50 MB transferred&lt;/td&gt;
&lt;td&gt;~50 MB transferred&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~50 MB transferred&lt;/td&gt;
&lt;td&gt;~50 MB transferred&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;rsync&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~50 MB transferred&lt;/td&gt;
&lt;td&gt;~500 KB transferred&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;rsync's advantage grows with project size. For a 500 MB project with small daily changes, rsync saves massive bandwidth and time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Considerations
&lt;/h2&gt;

&lt;p&gt;All three methods use SSH for encryption. The security fundamentals are the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt; : Use SSH key pairs instead of passwords&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port&lt;/strong&gt; : Default is 22 — consider changing it to reduce automated scanning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Key management&lt;/strong&gt; : Use dedicated deploy keys with restricted permissions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firewall&lt;/strong&gt; : Allow SSH only from known IP addresses&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Deploy Key Best Practice
&lt;/h3&gt;

&lt;p&gt;Create a dedicated key pair for deployments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;ssh-keygen -t ed25519 -f ~/.ssh/deploy_key -C "deploy@example.com"

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

&lt;/div&gt;



&lt;p&gt;On the server, restrict the key to specific commands in &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;command="rsync --server --daemon .",no-port-forwarding,no-X11-forwarding ssh-ed25519 AAAA... deploy@example.com

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  FTP vs SFTP
&lt;/h2&gt;

&lt;p&gt;If you're still using plain FTP, stop. FTP transmits passwords and file contents in plain text — anyone on the network can read them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;FTP&lt;/th&gt;
&lt;th&gt;SFTP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encryption&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;None (plain text)&lt;/td&gt;
&lt;td&gt;SSH encryption&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Password security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sent in clear text&lt;/td&gt;
&lt;td&gt;Encrypted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;File integrity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No verification&lt;/td&gt;
&lt;td&gt;Checksum verification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Firewall friendly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Requires multiple ports&lt;/td&gt;
&lt;td&gt;Single port (22)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resume support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no reason to use FTP in 2025. If your hosting provider only supports FTP, it's time to switch providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  How This Relates to Deployment
&lt;/h2&gt;

&lt;p&gt;When you deploy with &lt;a href="https://deployhq.com/features" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, you choose your transfer protocol during project setup. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; supports SFTP, FTP/S, and direct integrations with cloud services like S3 and DigitalOcean.&lt;/p&gt;

&lt;p&gt;For server deployments, SFTP is the default and recommended protocol. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; connects to your server via SSH, compares the files in your &lt;a href="https://deployhq.com/blog/what-is-git-and-why-should-you-use-it" rel="noopener noreferrer"&gt;Git repository&lt;/a&gt; with what's on the server, and transfers only the changed files — similar to how rsync works.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First deploy&lt;/strong&gt; transfers all files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subsequent deploys&lt;/strong&gt; only transfer changed files (fast and bandwidth-efficient)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deleted files&lt;/strong&gt; are removed from the server automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions&lt;/strong&gt; are preserved during transfer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you push from &lt;a href="https://deployhq.com/deploy-from-github" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://deployhq.com/deploy-from-gitlab" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles the file transfer securely. For &lt;a href="https://deployhq.com/for-agencies" rel="noopener noreferrer"&gt;agencies&lt;/a&gt; managing dozens of client servers, having a consistent, secure transfer method across all projects reduces operational risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is rsync better than SFTP for deployments?&lt;/strong&gt; For manual deployments, yes — rsync's delta algorithm is significantly faster for incremental updates. But deployment tools like &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; already implement similar delta logic over SFTP, so you get the speed benefit without writing rsync commands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I use rsync with a non-standard SSH port?&lt;/strong&gt; Yes: &lt;code&gt;rsync -avz -e "ssh -p 2222" src/ user@server:/dest/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does SFTP support file permissions?&lt;/strong&gt; Yes. SFTP can set permissions, ownership, and timestamps on remote files, similar to rsync's archive mode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is SCP really deprecated?&lt;/strong&gt; The SCP protocol is deprecated, but the &lt;code&gt;scp&lt;/code&gt; command still works — it just uses SFTP internally now. Your existing scripts won't break, but there's no reason to write new ones using &lt;code&gt;scp&lt;/code&gt; when &lt;code&gt;sftp&lt;/code&gt; or &lt;code&gt;rsync&lt;/code&gt; do everything better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which protocol does &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; use?&lt;/strong&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; supports SFTP (recommended), FTP/S, and various cloud provider APIs. SFTP connections use SSH key authentication for maximum security.&lt;/p&gt;




&lt;p&gt;For most deployments, SFTP is the safe default. For manual syncing of large directories, rsync saves significant time and bandwidth. And for everything else, let your deployment tool handle the transfer protocol for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://deployhq.com/signup" rel="noopener noreferrer"&gt;Try&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; free&lt;/strong&gt; — deploy via SFTP with automatic delta transfers on every push. See &lt;a href="https://deployhq.com/pricing" rel="noopener noreferrer"&gt;pricing&lt;/a&gt; for team plans.&lt;/p&gt;




&lt;p&gt;Questions? Reach out at &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;@deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>sftp</category>
      <category>scp</category>
      <category>rsync</category>
      <category>tutorials</category>
    </item>
    <item>
      <title>DeployHQ vs Laravel Forge: How They Differ and How to Use Them Together</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Wed, 11 Feb 2026 08:08:54 +0000</pubDate>
      <link>https://dev.to/deployhq/deployhq-vs-laravel-forge-how-they-differ-and-how-to-use-them-together-3n7f</link>
      <guid>https://dev.to/deployhq/deployhq-vs-laravel-forge-how-they-differ-and-how-to-use-them-together-3n7f</guid>
      <description>&lt;p&gt;If you're a Laravel developer, there's a good chance you've used &lt;a href="https://forge.laravel.com" rel="noopener noreferrer"&gt;Laravel Forge&lt;/a&gt; to provision and manage your servers. Forge is excellent at what it does — spinning up a fully configured server with Nginx, PHP, MySQL, Redis, and SSL in under a minute.&lt;/p&gt;

&lt;p&gt;But once your server is running, how does your code actually get there?&lt;/p&gt;

&lt;p&gt;Forge includes a basic push-to-deploy feature, but as your application grows — multiple environments, build steps, team workflows — you may find yourself wanting more control over the deployment process itself. That's where &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; comes in.&lt;/p&gt;

&lt;p&gt;This guide breaks down what each tool does, where they overlap, and how to use them together for a deployment workflow that covers both server management and code delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Laravel Forge Does
&lt;/h2&gt;

&lt;p&gt;Forge is a &lt;strong&gt;server provisioning and management&lt;/strong&gt; platform. Its primary job is turning a bare VPS from providers like DigitalOcean, AWS, or Hetzner into a fully configured application server.&lt;/p&gt;

&lt;p&gt;What Forge handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server provisioning&lt;/strong&gt; : One-click setup of Nginx, PHP (multiple versions), MySQL/PostgreSQL, Redis, Memcached&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL certificates&lt;/strong&gt; : Free Let's Encrypt certificates with auto-renewal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security&lt;/strong&gt; : Firewall configuration, SSH key management, automatic security updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process management&lt;/strong&gt; : Queue workers (Supervisor), cron job scheduling, daemon processes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring&lt;/strong&gt; : CPU, memory, and disk usage dashboards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Basic deployments&lt;/strong&gt; : Push-to-deploy from GitHub, GitLab, or Bitbucket with a configurable deploy script&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Forge's deployment feature runs a shell script on your server when you push to a branch. The default script does a &lt;code&gt;git pull&lt;/code&gt;, runs &lt;code&gt;composer install&lt;/code&gt;, and restarts PHP-FPM. For simple apps, this works fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What DeployHQ Does
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; is a &lt;strong&gt;deployment pipeline&lt;/strong&gt; platform. It focuses entirely on getting your code from a repository to one or more servers reliably, with &lt;a href="https://deployhq.com/blog/build-pipelines-in-deployhq-streamline-your-deployment-workflow" rel="noopener noreferrer"&gt;build pipelines&lt;/a&gt;, atomic releases, and full visibility into what was deployed and when.&lt;/p&gt;

&lt;p&gt;What &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build pipelines&lt;/strong&gt; : Run &lt;code&gt;composer install&lt;/code&gt;, &lt;code&gt;npm run build&lt;/code&gt;, asset compilation, and other build steps on DeployHQ's build servers — not on your production machine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic deployments&lt;/strong&gt; : Release-based deploys with instant rollback, so a failed deployment never leaves your site in a broken state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-server deploys&lt;/strong&gt; : Push to staging and production from the same pipeline with different configurations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment dashboard&lt;/strong&gt; : Visual history of every deployment, with logs, diffs, and status&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framework-agnostic&lt;/strong&gt; : Works with &lt;a href="https://deployhq.com/blog/5-powerful-ways-to-deploy-php-applications-with-deployhq" rel="noopener noreferrer"&gt;PHP&lt;/a&gt;, Node.js, Python, Ruby, .NET, and any other stack&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations&lt;/strong&gt; : &lt;a href="https://deployhq.com/blog/selectable-deployment-notifications" rel="noopener noreferrer"&gt;Deployment notifications&lt;/a&gt; to Slack, Discord, Teams, plus error tracking and cache purging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSH/SFTP/FTP support&lt;/strong&gt; : Deploys to any server you can connect to&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Side-by-Side Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;Forge&lt;/th&gt;
&lt;th&gt;DeployHQ&lt;/th&gt;
&lt;th&gt;Both Together&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server provisioning&lt;/td&gt;
&lt;td&gt;Yes — full stack setup&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Forge provisions, DeployHQ deploys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSL/domain management&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Forge handles SSL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP version management&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Forge manages PHP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Queue workers / cron&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Forge manages processes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build pipeline&lt;/td&gt;
&lt;td&gt;No — runs on server&lt;/td&gt;
&lt;td&gt;Yes — dedicated build servers&lt;/td&gt;
&lt;td&gt;DeployHQ builds, Forge serves&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Atomic/zero-downtime deploys&lt;/td&gt;
&lt;td&gt;Yes (new sites only)&lt;/td&gt;
&lt;td&gt;Yes (any project)&lt;/td&gt;
&lt;td&gt;DeployHQ handles releases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-server deployment&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Deploy to staging + production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Visual deployment history&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Detailed with logs and diffs&lt;/td&gt;
&lt;td&gt;Full visibility via DeployHQ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rollback&lt;/td&gt;
&lt;td&gt;Keep last 4 releases&lt;/td&gt;
&lt;td&gt;Instant rollback to any release&lt;/td&gt;
&lt;td&gt;DeployHQ rollbacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framework support&lt;/td&gt;
&lt;td&gt;PHP-focused&lt;/td&gt;
&lt;td&gt;Any language/framework&lt;/td&gt;
&lt;td&gt;Full flexibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Third-party integrations&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Extensive (Slack, Discord, Bugsnag, Cloudflare)&lt;/td&gt;
&lt;td&gt;DeployHQ integrations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How They Work Together
&lt;/h2&gt;

&lt;p&gt;The ideal setup uses each tool for what it does best: Forge manages your infrastructure, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; manages your deployments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    subgraph Developer
        A["Git Push to GitHub/GitLab"]
    end

    subgraph DeployHQ ["DeployHQ (Build &amp;amp; Deploy)"]
        B["Detect Push"]
        C["Run Build Pipeline"]
        D["composer install"]
        E["npm run build"]
        F["php artisan optimize"]
        G["Deploy via SSH"]
    end

    subgraph Forge ["Forge-Provisioned Server"]
        H["Nginx + PHP-FPM"]
        I["MySQL / PostgreSQL"]
        J["Redis + Queue Workers"]
        K["SSL + Firewall"]
    end

    A --&amp;gt; B
    B --&amp;gt; C
    C --&amp;gt; D --&amp;gt; E --&amp;gt; F --&amp;gt; G
    G --&amp;gt; H
    H --&amp;gt; I
    H --&amp;gt; J
    H --&amp;gt; K

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up DeployHQ With a Forge Server
&lt;/h2&gt;

&lt;p&gt;Here's how to connect &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; to a server provisioned by Forge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Provision Your Server on Forge
&lt;/h3&gt;

&lt;p&gt;Create your server on Forge as usual — choose your provider, select your PHP version, and let Forge set up the full stack. Make note of the server's IP address.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Get Your DeployHQ SSH Public Key
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, go to &lt;strong&gt;your project&lt;/strong&gt; (or create a new one) and navigate to the &lt;strong&gt;Server&lt;/strong&gt; settings. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; will show you its SSH public key. Copy it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Add the SSH Key to Your Forge Server
&lt;/h3&gt;

&lt;p&gt;In Forge, go to your server's &lt;strong&gt;SSH Keys&lt;/strong&gt; section and add DeployHQ's public key. This authorizes &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; to connect and deploy files. For more on SSH key setup, see our guide on &lt;a href="https://deployhq.com/blog/5-ways-to-create-ssh-keys-from-the-command-line-for-deployhq" rel="noopener noreferrer"&gt;creating SSH keys for DeployHQ&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Create a DeployHQ Server Entry
&lt;/h3&gt;

&lt;p&gt;Back in &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;, configure the server connection:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protocol&lt;/strong&gt; : SSH/SFTP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hostname&lt;/strong&gt; : Your Forge server's IP address&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Port&lt;/strong&gt; : 22&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Username&lt;/strong&gt; : &lt;code&gt;forge&lt;/code&gt; (Forge's default deploy user)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy path&lt;/strong&gt; : &lt;code&gt;/home/forge/your-site.com/current&lt;/code&gt; (or your site's root)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 5: Configure Your Build Pipeline
&lt;/h3&gt;

&lt;p&gt;This is where &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; shines. Instead of running build commands on your production server, configure them in DeployHQ's build pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--optimize-autoloader&lt;/span&gt;
npm ci
npm run build

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

&lt;/div&gt;



&lt;p&gt;These run on DeployHQ's build servers, keeping your production server lean.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Configure SSH Commands
&lt;/h3&gt;

&lt;p&gt;Add post-deployment SSH commands to run on your Forge server after files are transferred:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /home/forge/your-site.com
php artisan migrate &lt;span class="nt"&gt;--force&lt;/span&gt;
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan queue:restart

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Enable Zero-Downtime Deployments
&lt;/h3&gt;

&lt;p&gt;In your &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; project, enable &lt;a href="https://deployhq.com/blog/migrate-deployhq-project-to-use-atomic-deployments" rel="noopener noreferrer"&gt;atomic deployments&lt;/a&gt; for release-based deploys with instant rollback. This pairs well with &lt;a href="https://deployhq.com/blog/database-migration-strategies-for-zero-downtime-deployments-a-step-by-step-guide" rel="noopener noreferrer"&gt;database migration strategies for zero-downtime&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Disable Forge's Push-to-Deploy
&lt;/h3&gt;

&lt;p&gt;To avoid double deployments, disable Forge's built-in push-to-deploy feature for this site. Go to your site in Forge, navigate to &lt;strong&gt;Deployments&lt;/strong&gt; , and toggle off &lt;strong&gt;Quick Deploy&lt;/strong&gt;. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; will handle triggering deployments from now on.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Complete Laravel Deployment Example
&lt;/h2&gt;

&lt;p&gt;Here's what a full deployment looks like with both tools working together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build phase (runs on DeployHQ build servers)&lt;/span&gt;
composer &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--optimize-autoloader&lt;/span&gt; &lt;span class="nt"&gt;--no-interaction&lt;/span&gt;
npm ci &lt;span class="nt"&gt;--production&lt;/span&gt;
npm run build

&lt;span class="c"&gt;# After deployment (SSH commands on Forge server)&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /home/forge/your-site.com
php artisan down &lt;span class="nt"&gt;--retry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60
php artisan migrate &lt;span class="nt"&gt;--force&lt;/span&gt;
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
php artisan storage:link
php artisan up
php artisan queue:restart

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

&lt;/div&gt;



&lt;p&gt;With &lt;a href="https://deployhq.com/blog/zero-downtime-deployments-with-deployhq-a-step-by-step-guide" rel="noopener noreferrer"&gt;zero-downtime deployments&lt;/a&gt; enabled, the &lt;code&gt;artisan down&lt;/code&gt;/&lt;code&gt;up&lt;/code&gt; commands become optional — your users will never see a maintenance page.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Forge Alone
&lt;/h2&gt;

&lt;p&gt;Forge's built-in deployment works well if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a &lt;strong&gt;single server&lt;/strong&gt; with a &lt;strong&gt;single environment&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your app has &lt;strong&gt;no build step&lt;/strong&gt; (no npm, no webpack, no Vite)&lt;/li&gt;
&lt;li&gt;You're comfortable with &lt;code&gt;git pull&lt;/code&gt; happening directly on your server&lt;/li&gt;
&lt;li&gt;You don't need deployment rollbacks or multi-server deploys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a straightforward Laravel API or a small site, Forge's push-to-deploy is often enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Add DeployHQ
&lt;/h2&gt;

&lt;p&gt;Consider pairing with &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;build steps&lt;/strong&gt; like &lt;code&gt;npm run build&lt;/code&gt; or &lt;code&gt;composer install --no-dev&lt;/code&gt; and don't want them running on production&lt;/li&gt;
&lt;li&gt;You deploy to &lt;strong&gt;multiple servers&lt;/strong&gt; (staging, production, or multiple regions)&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;visual deployment history&lt;/strong&gt; with detailed logs and diffs&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;instant rollback&lt;/strong&gt; without SSH-ing into your server&lt;/li&gt;
&lt;li&gt;Your team needs &lt;strong&gt;deployment notifications&lt;/strong&gt; in Slack, Discord, or Teams&lt;/li&gt;
&lt;li&gt;You work with &lt;strong&gt;multiple frameworks&lt;/strong&gt; — not just Laravel — and want a unified deployment tool&lt;/li&gt;
&lt;li&gt;You manage client projects as an &lt;a href="https://deployhq.com/for-agencies" rel="noopener noreferrer"&gt;agency&lt;/a&gt; and need consistent workflows across different stacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a deeper look at PHP deployment strategies, see our &lt;a href="https://deployhq.com/blog/comparing-php-application-servers-in-2025-performance-scalability-and-modern-options" rel="noopener noreferrer"&gt;comparison of PHP application servers&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Environment Variables
&lt;/h2&gt;

&lt;p&gt;One area that needs attention when using both tools is environment variable management. Forge manages your &lt;code&gt;.env&lt;/code&gt; file on the server directly — you edit it through Forge's UI.&lt;/p&gt;

&lt;p&gt;When &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; deploys, it should &lt;strong&gt;not overwrite&lt;/strong&gt; the &lt;code&gt;.env&lt;/code&gt; file. Add &lt;code&gt;.env&lt;/code&gt; to your &lt;strong&gt;excluded files&lt;/strong&gt; list in DeployHQ's server configuration. This way, Forge keeps managing your environment secrets while &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles code delivery.&lt;/p&gt;

&lt;p&gt;For advanced setups, you can also use &lt;a href="https://deployhq.com/blog/how-to-deploy-php-applications-with-encrypted-environment-variables-using-dotenvx-and-deployhq" rel="noopener noreferrer"&gt;encrypted environment variables with Dotenvx&lt;/a&gt; to commit encrypted &lt;code&gt;.env&lt;/code&gt; files directly to your repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I use DeployHQ's build pipeline with Forge's zero-downtime deployments?
&lt;/h3&gt;

&lt;p&gt;It's better to choose one tool for deployment management. If you're using &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; for deployments, disable Forge's deploy feature and use DeployHQ's atomic deployments instead. Running both will cause conflicts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does DeployHQ replace Forge?
&lt;/h3&gt;

&lt;p&gt;No. They serve different purposes. Forge provisions and manages your server infrastructure (PHP, Nginx, SSL, databases, queues). &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles the deployment pipeline (building, transferring, and releasing code). You still need Forge (or manual server setup) for the server itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about Laravel Envoyer?
&lt;/h3&gt;

&lt;p&gt;Envoyer is Laravel's dedicated zero-downtime deployment tool. It sits between Forge's basic deploys and a full tool like &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; offers broader framework support, built-in build pipelines, and more integrations. If you only deploy Laravel apps, Envoyer is a solid option. If you work across multiple stacks, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; is more versatile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I deploy to a Forge server via &lt;a href="https://deployhq.com/blog/freelance-freedom-ditch-ssh-ftp-for-effortless-deployments-with-deployhq" rel="noopener noreferrer"&gt;FTP&lt;/a&gt;?
&lt;/h3&gt;

&lt;p&gt;Yes, but SSH/SFTP is recommended. Forge servers come with SSH configured, and DeployHQ's SSH deployment is faster and more secure than FTP. FTP should only be used if SSH is unavailable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I still need to manage SSH keys on Forge?
&lt;/h3&gt;

&lt;p&gt;Yes. You'll add DeployHQ's public SSH key to your Forge server so &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; can connect. After that, deployments are fully automated — no manual SSH required.&lt;/p&gt;




&lt;p&gt;Ready to level up your Laravel deployment workflow? &lt;a href="https://deployhq.com/signup" rel="noopener noreferrer"&gt;Sign up for DeployHQ&lt;/a&gt; and connect it to your Forge servers in minutes. For questions, reach out to &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;X @deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>deployhq</category>
      <category>forge</category>
      <category>php</category>
      <category>tutorials</category>
    </item>
    <item>
      <title>What's New in PHP 2026: Modern Features for Production</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Mon, 09 Feb 2026 10:54:09 +0000</pubDate>
      <link>https://dev.to/deployhq/whats-new-in-php-2026-modern-features-for-production-457n</link>
      <guid>https://dev.to/deployhq/whats-new-in-php-2026-modern-features-for-production-457n</guid>
      <description>&lt;p&gt;The PHP ecosystem is experiencing a renaissance. After years of steady improvements, PHP 8.5 and the upcoming PHP 8.6 are introducing features that fundamentally change how we write and deploy production applications. If you're still thinking of PHP as &lt;q&gt;that language from 2010,&lt;/q&gt; it's time to look again.&lt;/p&gt;

&lt;p&gt;The practical implication for engineering teams is bigger than syntax. Language-level improvements now directly affect deployment safety, debugging speed, and how quickly teams can ship changes with confidence. Features like the pipe operator and improved fatal-error backtraces can reduce friction in both day-to-day development and incident response.&lt;/p&gt;

&lt;p&gt;For teams running production workloads, the key is not chasing every new feature immediately. The goal is a controlled migration path: verify compatibility in staging, adopt stable features incrementally, and delay pre-GA features until they are finalized. This guide focuses on that production-first approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Evolution Nobody Expected
&lt;/h2&gt;

&lt;p&gt;For development teams managing production deployments, staying current with language features isn't just about writing cleaner code—it's about maintaining competitive advantage. While your deployment pipeline might be humming along perfectly with PHP 8.3, the features landing in 2026 will change how you architect, test, and scale your applications.&lt;/p&gt;

&lt;p&gt;The question isn't whether to upgrade. It's how to do it safely, without breaking production.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Actually New in PHP 8.5 and 8.6?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  PHP 8.5: Available Now (Released November 2025)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pipe Operator&lt;/strong&gt; The pipe operator enables functional composition, letting you chain operations without nested function calls:&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;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This replaces the nested pyramid of doom and makes data transformation pipelines readable top-to-bottom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Object Cloning with Data&lt;/strong&gt; PHP 8.5 also introduces clone improvements that allow property reassignment during cloning through the new &lt;code&gt;withProperties&lt;/code&gt; parameter. This reduces clone-then-modify boilerplate and makes object updates more explicit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Closures in Attributes&lt;/strong&gt; Decorators just got more powerful:&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="na"&gt;#[Route(path: '/api/users', middleware: fn() =&amp;gt; new AuthMiddleware())]&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This enables more expressive metadata and reduces boilerplate in framework code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fatal Error Backtraces&lt;/strong&gt; Stack traces now display for fatal errors. If you've ever debugged a segfault or memory exhaustion issue in production, you know why this matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  PHP 8.6: Targeted for Late 2026
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Partial Function Application (PFA)&lt;/strong&gt;Partial function application is being developed for PHP 8.6 and can significantly improve reusable callables for functional workflows. Because this is still evolving, production teams should treat syntax details as provisional until final GA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern Matching (Under Discussion)&lt;/strong&gt;Pattern matching has also been discussed for future versions, but production planning should assume this is not available until a final RFC is accepted and released.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why These Features Matter for Production Teams
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Deployment Safety Through Readable Code
&lt;/h3&gt;

&lt;p&gt;The pipe operator and improved diagnostics make transformation code and runtime failures easier to reason about. During code reviews and incident response, you can trace data flow at a glance. Less cognitive load means fewer deployment mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Performance Without Complexity
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://frankenphp.dev/" rel="noopener noreferrer"&gt;FrankenPHP&lt;/a&gt; is a modern PHP runtime that many teams are evaluating in 2026. Depending on workload and architecture, it can improve throughput and latency, especially with worker mode and long-running processes. Measure in your own environment before committing to migration.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Migration Path Clarity
&lt;/h3&gt;

&lt;p&gt;These features are backwards-compatible. You can deploy PHP 8.5 today, test thoroughly, and adopt features incrementally. Your existing PHP 8.3 code runs unchanged while you modernize hot paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Migration Scenarios
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scenario 1: API Data Transformation Pipeline
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (PHP 8.3):&lt;/strong&gt;&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nv"&gt;$mapped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;strtoupper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$filtered&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;array_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$mapped&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (PHP 8.5):&lt;/strong&gt;&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'active'&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;strtoupper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;])],&lt;/span&gt;
            &lt;span class="nv"&gt;$x&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;array_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The logic is identical, but the after version reads like a declarative pipeline. When this breaks at 3am, you'll appreciate the difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Configuration Object Updates
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&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;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;clone&lt;/span&gt; &lt;span class="nv"&gt;$baseConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cacheEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (PHP 8.5):&lt;/strong&gt;&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;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;clone&lt;/span&gt; &lt;span class="nv"&gt;$baseConfig&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'database'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'debug'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'cacheEnabled'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;One atomic operation. Less room for partial updates to slip through during deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 3: Logger Helper with Less Repetition
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Before (PHP 8.3):&lt;/strong&gt;&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;$logger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFO'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'User logged in'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFO'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Session created'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFO'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Cache warmed'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (works today):&lt;/strong&gt;&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;$info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$logger&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFO'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'User logged in'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Session created'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Cache warmed'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Less repetition, more maintainable, and easier to refactor. If PFA lands in stable PHP 8.6, this pattern can become even cleaner.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Deploy PHP 8.5/8.6 Safely
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Test in a Build Pipeline
&lt;/h3&gt;

&lt;p&gt;DeployHQ's &lt;a href="https://www.deployhq.com/features/build-pipelines" rel="noopener noreferrer"&gt;build pipelines&lt;/a&gt; let you specify PHP versions for each project. Set up a staging deployment with PHP 8.5, run your full test suite, and verify behavior before touching production.&lt;/p&gt;

&lt;p&gt;Configure your &lt;code&gt;.deployhq.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;php_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8.5&lt;/span&gt;
  &lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;composer install --no-dev&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;php artisan test&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Gradual Rollout Strategy
&lt;/h3&gt;

&lt;p&gt;Use &lt;a href="https://www.deployhq.com/features/zero-downtime-deployments" rel="noopener noreferrer"&gt;zero-downtime deployments&lt;/a&gt; to update servers one at a time. If a server with PHP 8.5 shows errors, rollback is instant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Monitor Fatal Error Backtraces
&lt;/h3&gt;

&lt;p&gt;PHP 8.5's new fatal error backtraces will surface issues you couldn't debug before. Integrate with your error monitoring (Sentry, Rollbar) and watch for new patterns during the first week post-deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Adopt Features Incrementally
&lt;/h3&gt;

&lt;p&gt;Don't rewrite everything. Start with new code using PHP 8.5 features like pipe operators and improved diagnostics. Refactor hot paths during regular maintenance. After 3-6 months, you'll have a modern codebase without a risky &lt;q&gt;big bang&lt;/q&gt; deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  DeployHQ and PHP 2026
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; supports &lt;a href="https://www.deployhq.com/support/build-pipelines/language-versions" rel="noopener noreferrer"&gt;custom PHP versions&lt;/a&gt; in build environments, making PHP 8.5 and 8.6 testing straightforward. Key deployment capabilities for PHP 2026 features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build Pipelines&lt;/strong&gt; : Test new PHP versions before production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Downtime Releases&lt;/strong&gt; : Roll out PHP upgrades without user impact&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Deployments&lt;/strong&gt; : All-or-nothing deployment prevents partial updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollback Support&lt;/strong&gt; : Instant revert if issues surface post-deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whether you're deploying Laravel with &lt;a href="https://www.deployhq.com/guides/laravel" rel="noopener noreferrer"&gt;our Laravel guide&lt;/a&gt; or custom PHP applications using &lt;a href="https://dev.to/deployhq/5-powerful-ways-to-deploy-php-applications-with-deployhq-421d"&gt;5 powerful deployment methods&lt;/a&gt;, &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; handles version transitions smoothly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PHP 8.5 is production-ready&lt;/strong&gt; with pipe operator, enhanced cloning, and fatal error backtraces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP 8.6 (target late 2026)&lt;/strong&gt; may include partial function application and other features still in active RFC development&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backwards compatibility&lt;/strong&gt; means safe, incremental adoption without deployment risk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FrankenPHP&lt;/strong&gt; may provide meaningful performance gains depending on workload and runtime model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration path&lt;/strong&gt; : Test in staging with build pipelines, deploy with zero-downtime strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Should I upgrade to PHP 8.5 immediately?
&lt;/h3&gt;

&lt;p&gt;Not necessarily. If your application is stable on PHP 8.3, wait until your next planned deployment window. Test thoroughly in staging first. The features are backwards-compatible, so there's no urgency—but there's also minimal risk once you've validated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Will PHP 8.5 break my existing code?
&lt;/h3&gt;

&lt;p&gt;Unlikely. The new features are additive. Your PHP 8.3 code should run unchanged on 8.5. However, always test in a build pipeline before deploying to production. Third-party dependencies might need updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I test PHP 8.5 without affecting production?
&lt;/h3&gt;

&lt;p&gt;Use DeployHQ's staging environments with custom PHP versions. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; your app to a staging server with PHP 8.5, run your test suite, and verify behavior. Only promote to production after validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the performance difference between PHP 8.3 and 8.5?
&lt;/h3&gt;

&lt;p&gt;Core language performance is similar (incremental improvements). The bigger gains often come from runtime choices and architecture (for example, FrankenPHP worker mode in suitable workloads). The new features also encourage clearer code that is easier to optimize and maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  When should I adopt the pipe operator and PFA?
&lt;/h3&gt;

&lt;p&gt;Start using pipe operators in new code immediately after upgrading to PHP 8.5. For PFA, wait for final stable PHP 8.6 syntax before broad adoption. Refactor existing code during regular maintenance cycles—prioritize hot paths and complex data transformations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to update Composer packages for PHP 8.5?
&lt;/h3&gt;

&lt;p&gt;Check your dependencies with &lt;code&gt;composer outdated&lt;/code&gt;. Most popular packages already support PHP 8.5. Laravel, Symfony, and WordPress are all compatible. If a package doesn't support 8.5, open an issue or find alternatives—community support for 8.5 is strong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use PHP 8.6 features today?
&lt;/h3&gt;

&lt;p&gt;Not in production yet. PHP 8.6 is currently targeted for late 2026, and features may still change before GA. You can test pre-release versions locally, but avoid production deployment until stable release.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's the best deployment strategy for PHP version upgrades?
&lt;/h3&gt;

&lt;p&gt;Use zero-downtime deployments to update servers sequentially. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; to one server, monitor for 15-30 minutes, then continue. If issues arise, rollback is instant. This minimizes risk while maintaining availability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I switch to FrankenPHP now?
&lt;/h3&gt;

&lt;p&gt;FrankenPHP is production-ready for many new projects. For existing apps, evaluate whether observed gains in your workload justify migration effort. It pairs well with PHP 8.5 features, but PHP-FPM with Nginx remains a solid choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where can I get help with PHP 8.5 deployment issues?
&lt;/h3&gt;

&lt;p&gt;Check &lt;a href="https://www.deployhq.com/support" rel="noopener noreferrer"&gt;DeployHQ's support documentation&lt;/a&gt; for build pipeline and deployment configuration. For account-specific help, reach out to &lt;a href="https://www.deployhq.com/contact" rel="noopener noreferrer"&gt;DeployHQ support&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;Ready to try PHP 8.5 in your deployment pipeline?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read the official PHP 8.5 release notes&lt;/strong&gt; on php.net (linked below)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set up a staging environment&lt;/strong&gt; with &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; build pipelines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test your application&lt;/strong&gt; with PHP 8.5 before deploying to production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor performance and errors&lt;/strong&gt; after deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incrementally adopt new features&lt;/strong&gt; in your codebase&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h3&gt;
  
  
  Official Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.php.net/releases/8.5/en.php" rel="noopener noreferrer"&gt;PHP 8.5 Release Notes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.php.net/manual/en/migration85.php" rel="noopener noreferrer"&gt;PHP 8.5 Migration Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.php.net/todo/php86" rel="noopener noreferrer"&gt;PHP 8.6 Preparation Timetable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.php.net/rfc/partial_function_application_v2" rel="noopener noreferrer"&gt;Partial Function Application v2 RFC&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DeployHQ Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.deployhq.com/guides" rel="noopener noreferrer"&gt;DeployHQ Guides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.deployhq.com/blog/comparing-php-application-servers-in-2025-performance-scalability-and-modern-options" rel="noopener noreferrer"&gt;PHP Application Servers Comparison&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Ready to deploy PHP 8.5 safely?&lt;/strong&gt; &lt;a href="https://www.deployhq.com/signup" rel="noopener noreferrer"&gt;Start your free trial&lt;/a&gt; and test new PHP versions in isolated build pipelines before production. &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt; with confidence, rollback instantly if needed.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>infrastructure</category>
      <category>opensource</category>
      <category>php</category>
    </item>
    <item>
      <title>API Versioning and Deployment Strategies: Rolling Out Breaking Changes Safely</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Thu, 05 Feb 2026 06:50:35 +0000</pubDate>
      <link>https://dev.to/deployhq/api-versioning-and-deployment-strategies-rolling-out-breaking-changes-safely-264j</link>
      <guid>https://dev.to/deployhq/api-versioning-and-deployment-strategies-rolling-out-breaking-changes-safely-264j</guid>
      <description>&lt;p&gt;Rolling out API changes without breaking existing clients is one of the most challenging aspects of backend development. Whether you're adding new features, deprecating old endpoints, or making breaking changes, proper versioning strategies ensure smooth deployments. In this guide, you'll learn how to implement API versioning and safely deploy breaking changes using &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why API Versioning Matters
&lt;/h2&gt;

&lt;p&gt;APIs are contracts with your consumers. Breaking changes can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Crash mobile apps&lt;/strong&gt; that haven't updated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Break integrations&lt;/strong&gt; with third-party services&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frustrate developers&lt;/strong&gt; who depend on your API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Damage trust&lt;/strong&gt; with API consumers
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    subgraph "Without Versioning"
        A[API Change] --&amp;gt; B[All Clients Break]
        B --&amp;gt; C[Angry Users]
    end

    subgraph "With Versioning"
        D[API Change v2] --&amp;gt; E[v1 Still Works]
        E --&amp;gt; F[Clients Migrate Gradually]
        F --&amp;gt; G[Happy Users]
    end

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Versioning Strategies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. URL Path Versioning
&lt;/h3&gt;

&lt;p&gt;The most common and visible approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;
&lt;span class="nx"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;


&lt;span class="c1"&gt;// Express.js implementation&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v1Router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./routes/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v2Router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./routes/v2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v1Router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v2Router&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt; : Clear, cache-friendly, easy to understand &lt;strong&gt;Cons&lt;/strong&gt; : Duplicates route structure, longer URLs&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Header Versioning
&lt;/h3&gt;

&lt;p&gt;Version specified in request headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;
&lt;span class="nx"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;vnd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;


&lt;span class="c1"&gt;// Express.js middleware&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;versionMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;acceptHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;accept&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;versionMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;acceptHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/vnd&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;myapi&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;v&lt;/span&gt;&lt;span class="se"&gt;(\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;versionMatch&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;versionMatch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getUsersV2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;getUsersV1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt; : Clean URLs, follows HTTP standards &lt;strong&gt;Cons&lt;/strong&gt; : Hidden versioning, harder to test&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Query Parameter Versioning
&lt;/h3&gt;

&lt;p&gt;Version in query string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/users?version=2

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros&lt;/strong&gt; : Simple implementation &lt;strong&gt;Cons&lt;/strong&gt; : Can be cached incorrectly, feels hacky&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Custom Header Versioning
&lt;/h3&gt;

&lt;p&gt;Most flexible approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;GET&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;
&lt;span class="nx"&gt;X&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;API&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;


&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;versionMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-api-version&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implementing Backward Compatibility
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Strategy 1: Additive Changes Only
&lt;/h3&gt;

&lt;p&gt;Safe changes that never break clients:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;v&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john@example.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;v&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(additive&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;safe)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"profile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;New&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;field&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"avatar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strategy 2: Response Transformation
&lt;/h3&gt;

&lt;p&gt;Transform responses based on version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// services/userTransformer.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transformers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;

  &lt;span class="na"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatarUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;biography&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;joinedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;transformUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transformer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transformers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;`v&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;transformers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Strategy 3: Dual-Write Pattern
&lt;/h3&gt;

&lt;p&gt;Support both old and new fields during migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Accept both formats during transition&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Support legacy 'name' field and new 'firstName/lastName'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fullName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// Save user...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deployment Workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant Dev as Development
    participant Stage as Staging
    participant Prod as Production
    participant Clients as API Clients

    Dev-&amp;gt;&amp;gt;Stage: Deploy v2 alongside v1
    Stage-&amp;gt;&amp;gt;Stage: Test both versions
    Stage-&amp;gt;&amp;gt;Prod: Deploy v2 (v1 still active)

    Note over Prod,Clients: Transition Period
    Prod-&amp;gt;&amp;gt;Clients: Announce v2 availability
    Clients-&amp;gt;&amp;gt;Prod: Migrate to v2
    Prod-&amp;gt;&amp;gt;Clients: Deprecation warning for v1

    Note over Prod: After migration deadline
    Prod-&amp;gt;&amp;gt;Prod: Remove v1

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  DeployHQ Deployment Strategy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Side-by-Side Version Deployment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# deploy.sh - Deploy new version alongside existing&lt;/span&gt;

&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="nv"&gt;DEPLOY_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/www/api"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deploying API version &lt;/span&gt;&lt;span class="nv"&gt;$VERSION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Deploy new version code&lt;/span&gt;
rsync &lt;span class="nt"&gt;-avz&lt;/span&gt; &lt;span class="nt"&gt;--delete&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'node_modules'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'.env'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    ./ &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_PATH&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;

&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$DEPLOY_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
npm ci &lt;span class="nt"&gt;--production&lt;/span&gt;

&lt;span class="c"&gt;# Run migrations (if needed)&lt;/span&gt;
npm run migrate

&lt;span class="c"&gt;# Restart application&lt;/span&gt;
pm2 reload api

&lt;span class="c"&gt;# Verify both versions work&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Testing v1..."&lt;/span&gt;
curl &lt;span class="nt"&gt;-sf&lt;/span&gt; http://localhost:3000/api/v1/health &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Testing v2..."&lt;/span&gt;
curl &lt;span class="nt"&gt;-sf&lt;/span&gt; http://localhost:3000/api/v2/health &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deployment complete!"&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Feature Flag for Gradual Rollout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// middleware/featureFlags.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;useNewUserEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEW_USER_ENDPOINT&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;percentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEW_USER_ENDPOINT_PERCENTAGE&lt;/span&gt;&lt;span class="p"&gt;)&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="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;shouldUseFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flagName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;flagName&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;flag&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Percentage rollout based on user ID&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hashUserId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percentage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage in route&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shouldUseFeature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;useNewUserEndpoint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;newGetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;legacyGetUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For more on feature flags, see &lt;a href="https://deployhq.com/blog/what-are-feature-flags" rel="noopener noreferrer"&gt;what are feature flags&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deprecation Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Deprecation Headers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// middleware/deprecation.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deprecatedEndpoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /api/v1/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;deprecatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2024-01-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;sunsetAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2024-06-01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /api/v2/users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;deprecationMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deprecation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;deprecatedEndpoints&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deprecation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Deprecation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`date="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deprecation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deprecatedAt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sunset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deprecation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sunsetAt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deprecation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;; rel="successor-version"`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Log deprecation usage for monitoring&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Deprecated endpoint accessed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;clientIp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user-agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;next&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;h3&gt;
  
  
  Consumer Communication
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Track API version usage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;versionUsage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-client-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;versionUsage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;versionUsage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="nx"&gt;versionUsage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;versionUsage&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;]&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="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Endpoint to check usage&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/internal/api-usage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;versionUsage&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Contract Testing
&lt;/h2&gt;

&lt;p&gt;Ensure backward compatibility with contract tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/contracts/v1.test.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;supertest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API v1 Contract&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /api/v1/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns expected v1 schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1/users/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// v1 contract: must have these fields&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// v1 contract: must NOT have v2 fields&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API v2 Contract&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;GET /api/v2/users/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;returns expected v2 schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v2/users/1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// v2 contract: must have all fields&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;have&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;avatar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  DeployHQ Build Commands with Contract Tests
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install dependencies&lt;/span&gt;
npm ci

&lt;span class="c"&gt;# Run unit tests&lt;/span&gt;
npm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Run contract tests (critical for versioning)&lt;/span&gt;
npm run &lt;span class="nb"&gt;test&lt;/span&gt;:contracts

&lt;span class="c"&gt;# Build&lt;/span&gt;
npm run build

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  OpenAPI Documentation
&lt;/h2&gt;

&lt;p&gt;Keep documentation in sync with versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# openapi/v1.yaml&lt;/span&gt;
&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.0.0&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;My API&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;

&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/users/{id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get user by ID (v1)&lt;/span&gt;
      &lt;span class="na"&gt;deprecated&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;200&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/UserV1'&lt;/span&gt;

&lt;span class="c1"&gt;# openapi/v2.yaml&lt;/span&gt;
&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.0.0&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;My API&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2.0.0&lt;/span&gt;

&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;/users/{id}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Get user by ID (v2)&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;200&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/UserV2'&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Version Sunset Checklist
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[Announce Deprecation] --&amp;gt; B[Add Deprecation Headers]
    B --&amp;gt; C[Monitor v1 Usage]
    C --&amp;gt; D{Usage Below Threshold?}
    D --&amp;gt;|No| E[Reach Out to Heavy Users]
    E --&amp;gt; C
    D --&amp;gt;|Yes| F[Send Final Warning]
    F --&amp;gt; G[Remove v1]
    G --&amp;gt; H[Update Documentation]

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Automated Monitoring
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Monitor version usage and alert&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkVersionUsage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getVersionStats&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Alert if v1 usage is still high before sunset&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percentage&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nf"&gt;isWithinSunsetWindow&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendAlert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;version-sunset-warning&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`v1 still has &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;percentage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% traffic`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;topClients&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;topClients&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Run daily&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkVersionUsage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Choose a versioning strategy&lt;/strong&gt; and stick with it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make additive changes&lt;/strong&gt; whenever possible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never remove fields&lt;/strong&gt; without versioning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add deprecation headers&lt;/strong&gt; before removing endpoints&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run contract tests&lt;/strong&gt; on every deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor version usage&lt;/strong&gt; to plan migrations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Communicate changes&lt;/strong&gt; clearly to consumers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provide migration guides&lt;/strong&gt; for breaking changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set clear sunset dates&lt;/strong&gt; and enforce them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep documentation updated&lt;/strong&gt; for all versions&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Ready to implement API versioning? Here's your checklist:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose your versioning strategy (URL path recommended)&lt;/li&gt;
&lt;li&gt;Set up version routing&lt;/li&gt;
&lt;li&gt;Implement response transformers&lt;/li&gt;
&lt;li&gt;Add contract tests to your pipeline&lt;/li&gt;
&lt;li&gt;Set up deprecation monitoring&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For zero-downtime deployment strategies, see our guide on &lt;a href="https://deployhq.com/blog/zero-downtime-deployments-keeping-your-application-running-smoothly" rel="noopener noreferrer"&gt;zero-downtime deployments&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Questions about API versioning? Contact &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or follow &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;@deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>infrastructure</category>
      <category>security</category>
      <category>apiversioning</category>
    </item>
    <item>
      <title>Context7 Guide: Stop AI Hallucinations with Live Docs</title>
      <dc:creator>DeployHQ</dc:creator>
      <pubDate>Mon, 02 Feb 2026 19:47:14 +0000</pubDate>
      <link>https://dev.to/deployhq/context7-guide-stop-ai-hallucinations-with-live-docs-1330</link>
      <guid>https://dev.to/deployhq/context7-guide-stop-ai-hallucinations-with-live-docs-1330</guid>
      <description>&lt;p&gt;You ask your AI assistant to help you set up authentication with Supabase. It confidently generates a full implementation using &lt;code&gt;createClient()&lt;/code&gt; with options that haven't existed for two major versions. You spend the next hour debugging code that looks correct but simply doesn't work.&lt;/p&gt;

&lt;p&gt;Sound familiar?&lt;/p&gt;

&lt;p&gt;This is the hidden tax of AI-assisted coding. LLMs are trained on data that's often a year or more out of date, and frameworks like Next.js, React, and Rails move fast. That &lt;q&gt;helpful&lt;/q&gt; code suggestion might use deprecated methods, removed parameters, or entirely hallucinated APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context7&lt;/strong&gt; solves this problem by injecting current, version-specific documentation directly into your AI's context—so you get working code on the first try.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Context7?
&lt;/h2&gt;

&lt;p&gt;Context7 is an &lt;a href="https://deployhq.com/blog/what-is-an-mcp-server-complete-guide-for-developers" rel="noopener noreferrer"&gt;MCP (Model Context Protocol)&lt;/a&gt; server built by &lt;a href="https://upstash.com" rel="noopener noreferrer"&gt;Upstash&lt;/a&gt; that acts as a bridge between your AI coding assistant and up-to-date library documentation.&lt;/p&gt;

&lt;p&gt;When you include &lt;code&gt;use context7&lt;/code&gt; in your prompt, the server identifies which library you're asking about, fetches the latest official documentation, and injects the relevant code examples directly into the AI's context window.&lt;/p&gt;

&lt;p&gt;The result? Your AI assistant bases its suggestions on actual current APIs rather than guessing from stale training data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sequenceDiagram
    participant Dev as Developer
    participant AI as AI Assistant
    participant C7 as Context7 MCP
    participant Docs as Library Docs

    Dev-&amp;gt;&amp;gt;AI: "Set up Supabase auth. use context7"
    AI-&amp;gt;&amp;gt;C7: Resolve library: supabase
    C7-&amp;gt;&amp;gt;Docs: Fetch current documentation
    Docs--&amp;gt;&amp;gt;C7: v2.x API reference + examples
    C7--&amp;gt;&amp;gt;AI: Inject version-specific context
    AI--&amp;gt;&amp;gt;Dev: Working code using current APIs

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Problem It Solves
&lt;/h3&gt;

&lt;p&gt;AI coding assistants have a fundamental limitation: their knowledge comes from training data with a cutoff date. Libraries evolve constantly—APIs change, methods get renamed, entire patterns get deprecated.&lt;/p&gt;

&lt;p&gt;Here's what happens without Context7:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Outdated code examples&lt;/strong&gt; based on old training data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hallucinated APIs&lt;/strong&gt; that don't actually exist&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generic answers&lt;/strong&gt; that don't match your specific library version&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wasted time&lt;/strong&gt; debugging code that looked correct&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With Context7, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version-specific documentation&lt;/strong&gt; pulled from the source&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Current code examples&lt;/strong&gt; that actually work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accurate method signatures&lt;/strong&gt; and patterns&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Working code on the first try&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've been following our coverage of &lt;a href="https://deployhq.com/blog/6-must-have-mcp-servers-for-web-developers-in-2025" rel="noopener noreferrer"&gt;MCP servers for web developers&lt;/a&gt;, you'll recognise Context7 as one of the essential tools we recommend. Now let's look at why it matters for your development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context7 in Action: Real Examples
&lt;/h2&gt;

&lt;p&gt;The best way to understand Context7's value is to see it work. Here are scenarios where it shines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: Next.js Middleware
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Without Context7&lt;/strong&gt; , your AI might generate middleware using patterns from Next.js 12 or 13 when you're actually using version 15.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With Context7&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a Next.js middleware that checks for a valid JWT in cookies
and redirects unauthenticated users to /login. use context7

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

&lt;/div&gt;



&lt;p&gt;Context7 fetches the current Next.js 15 middleware documentation and your AI generates code using the correct &lt;code&gt;NextResponse&lt;/code&gt; patterns, proper matcher configuration, and current edge runtime APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 2: Rails API Authentication
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Set up JWT authentication for a Rails 7.2 API using the jwt gem,
including refresh token rotation. use context7

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

&lt;/div&gt;



&lt;p&gt;Instead of getting code that might work with older Rails versions, you get implementations that align with current Rails conventions and the latest jwt gem API—perfect for &lt;a href="https://deployhq.com/guides/ruby-on-rails" rel="noopener noreferrer"&gt;Rails applications you're deploying&lt;/a&gt; through &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 3: Supabase Database Queries
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Implement a Supabase query that fetches all users with their
related posts, with pagination. use context7

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

&lt;/div&gt;



&lt;p&gt;Context7 ensures you're using the current Supabase JavaScript client methods, not deprecated patterns from v1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 4: Version-Specific Documentation
&lt;/h3&gt;

&lt;p&gt;Need documentation for a specific library version? Just mention it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;How do I configure TailwindCSS 4.0 with Vite? use context7

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

&lt;/div&gt;



&lt;p&gt;Context7 automatically matches the appropriate version and delivers relevant documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Started
&lt;/h2&gt;

&lt;p&gt;Setting up Context7 takes just a few minutes. It works with all major AI coding tools including Cursor, Claude Desktop, Claude Code, and VS Code with MCP-compatible extensions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We've written a comprehensive step-by-step guide covering:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prerequisites and installation for each client&lt;/li&gt;
&lt;li&gt;Configuration for automatic invocation&lt;/li&gt;
&lt;li&gt;Advanced usage with direct library references&lt;/li&gt;
&lt;li&gt;Troubleshooting common issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://deployhq.com/guides/context7" rel="noopener noreferrer"&gt;Read the full Context7 setup guide&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pro Tip: Automatic Documentation Fetching
&lt;/h2&gt;

&lt;p&gt;One of the best features covered in our setup guide is configuring automatic invocation. Instead of typing &lt;code&gt;use context7&lt;/code&gt; every time, you can set up rules so your AI assistant automatically fetches current documentation for any library-related question.&lt;/p&gt;

&lt;p&gt;This means you write prompts naturally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a Stripe checkout session API route in Next.js 15 App Router

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

&lt;/div&gt;



&lt;p&gt;And Context7 kicks in automatically, ensuring the generated code uses current APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context7 and DeployHQ: The Complete Workflow
&lt;/h2&gt;

&lt;p&gt;Here's where things get interesting for web developers who deploy regularly.&lt;/p&gt;

&lt;h3&gt;
  
  
  From Accurate Code to Production
&lt;/h3&gt;

&lt;p&gt;Pairing Context7 with &lt;a href="https://deployhq.com/features/automatic-deployments" rel="noopener noreferrer"&gt;DeployHQ's automated deployments&lt;/a&gt; creates a streamlined development pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Write accurate code with Context7&lt;/strong&gt; : Generate working implementations on the first try using current APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push to your repository&lt;/strong&gt; : Commit your changes to &lt;a href="https://deployhq.com/deploy-from-github" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, GitLab, or Bitbucket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy automatically&lt;/strong&gt; : &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; detects the push and deploys to your server with zero downtime&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow eliminates two of the biggest time sinks in web development: debugging hallucinated AI code and manual deployment processes. For a deeper look at setting up this kind of pipeline, see our guide on &lt;a href="https://deployhq.com/blog/building-a-ci-cd-pipeline-from-scratch-with-deployhq-a-step-by-step-guide" rel="noopener noreferrer"&gt;building a CI/CD pipeline with DeployHQ&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Even Better with DeployHQ MCP
&lt;/h3&gt;

&lt;p&gt;If you're using Claude Code or another MCP-compatible assistant, you can take this further with the &lt;a href="https://deployhq.com/blog/introducing-the-deployhq-mcp-server-ai-powered-deployment-management" rel="noopener noreferrer"&gt;DeployHQ MCP Server&lt;/a&gt;. Combine it with Context7 and you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate accurate code using current documentation&lt;/li&gt;
&lt;li&gt;Trigger deployments directly from your AI assistant&lt;/li&gt;
&lt;li&gt;Check deployment status and history&lt;/li&gt;
&lt;li&gt;Roll back if needed—all without leaving your editor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's the complete &lt;a href="https://deployhq.com/blog/mcp-future-of-ai-devops" rel="noopener noreferrer"&gt;AI-powered development workflow&lt;/a&gt; we've been building towards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Libraries Available on Context7
&lt;/h2&gt;

&lt;p&gt;Context7 maintains documentation for thousands of libraries across all major programming languages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript/TypeScript&lt;/strong&gt; : Next.js, React, Vue, Svelte, Astro, Supabase, Firebase, Prisma, Tailwind CSS, Express, Fastify, Hono&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ruby&lt;/strong&gt; : Ruby on Rails, Sidekiq, Devise, Pundit&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt; : Django, FastAPI, Flask, SQLAlchemy, Pydantic&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And many more&lt;/strong&gt; —the library is community-contributed and constantly growing.&lt;/p&gt;

&lt;p&gt;If your favourite library isn't available, you can submit it through Context7's &lt;a href="https://context7.com/add-library" rel="noopener noreferrer"&gt;project addition guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Context7 addresses one of the most frustrating aspects of AI-assisted coding: the constant uncertainty about whether generated code actually works with your library versions.&lt;/p&gt;

&lt;p&gt;By injecting current, version-specific documentation into your AI's context, Context7 transforms your coding assistant from a sometimes-helpful tool into a reliable pair programming partner that always knows the current APIs.&lt;/p&gt;

&lt;p&gt;The setup takes a few minutes. The payoff is immediate: working code on the first try, no more debugging hallucinated methods, and no more tab-switching to verify that functions still exist.&lt;/p&gt;

&lt;p&gt;Combined with &lt;a href="https://deployhq.com/blog/continuous-deployment-with-deployhq" rel="noopener noreferrer"&gt;continuous deployment through DeployHQ&lt;/a&gt;, you get a development workflow where accurate code flows smoothly from your editor to production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read our full Context7 setup guide&lt;/strong&gt; — Detailed installation instructions for every client&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore our list of MCP servers for developers&lt;/strong&gt; — Context7 plus five other essential tools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://deployhq.com/features/deployhq-mcp" rel="noopener noreferrer"&gt;Try&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; MCP Server&lt;/strong&gt; — AI-powered deployment automation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://deployhq.com/signup" rel="noopener noreferrer"&gt;Start your free&lt;/a&gt;&lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; trial&lt;/strong&gt; — Automate your deployments in minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Context7 free to use?
&lt;/h3&gt;

&lt;p&gt;Yes, Context7 is free for personal and educational use. If you need higher rate limits or access to private repositories, you can get an API key from the &lt;a href="https://context7.com/dashboard" rel="noopener noreferrer"&gt;Context7 dashboard&lt;/a&gt;. See our setup guide for configuration details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which AI coding assistants work with Context7?
&lt;/h3&gt;

&lt;p&gt;Context7 works with any MCP-compatible client, including Cursor, Claude Desktop, Claude Code, VS Code (with Cline or similar extensions), Windsurf, and others. Check our setup guide for installation instructions for each client.&lt;/p&gt;

&lt;h3&gt;
  
  
  What programming languages does Context7 support?
&lt;/h3&gt;

&lt;p&gt;Context7 supports libraries across all major languages including JavaScript/TypeScript, Ruby, Python, Go, Java, and more. The library database is community-contributed and constantly growing. You can browse available libraries at context7.com.&lt;/p&gt;

&lt;h3&gt;
  
  
  How is Context7 different from just pasting documentation into my prompt?
&lt;/h3&gt;

&lt;p&gt;Context7 automatically identifies the relevant library, fetches version-specific documentation, and injects only the relevant code examples into your AI's context. This saves time and avoids hitting token limits with bloated documentation. It also ensures you always get current information rather than whatever you happened to copy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I add my own library's documentation to Context7?
&lt;/h3&gt;

&lt;p&gt;Yes! Context7 is community-driven. You can submit libraries through their project addition guide. Library maintainers can also claim and manage their own documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to type &lt;q&gt;use context7&lt;/q&gt; every time?
&lt;/h3&gt;

&lt;p&gt;No—you can configure automatic invocation so your AI assistant fetches documentation automatically for any library-related question. Our setup guide explains how to set this up for Cursor and Claude.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Context7 work offline?
&lt;/h3&gt;

&lt;p&gt;No, Context7 requires an internet connection to fetch documentation from its servers. The MCP server runs locally, but it needs to reach the Context7 API to retrieve library docs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use Context7 with the DeployHQ MCP Server?
&lt;/h3&gt;

&lt;p&gt;Absolutely. You can run multiple MCP servers simultaneously. Using Context7 alongside the &lt;a href="https://www.deployhq.com" rel="noopener noreferrer"&gt;DeployHQ&lt;/a&gt; MCP Server gives you accurate code generation plus deployment automation—all from your AI assistant.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://context7.com/docs/overview" rel="noopener noreferrer"&gt;Context7 Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/upstash/context7" rel="noopener noreferrer"&gt;Context7 GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://context7.com" rel="noopener noreferrer"&gt;Context7 Library Database&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Have questions about Context7 or deployment automation? Reach out to &lt;a href="mailto:support@deployhq.com"&gt;support@deployhq.com&lt;/a&gt; or find us on &lt;a href="https://x.com/deployhq" rel="noopener noreferrer"&gt;X @deployhq&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devopsinfrastructure</category>
      <category>tipstricks</category>
      <category>context7</category>
    </item>
  </channel>
</rss>
