{"id":4767,"date":"2026-04-21T21:47:00","date_gmt":"2026-04-21T16:17:00","guid":{"rendered":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/"},"modified":"2026-04-21T21:47:00","modified_gmt":"2026-04-21T16:17:00","slug":"10-devops-best-practices-for-faster-software-delivery-4","status":"publish","type":"post","link":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/","title":{"rendered":"10 DevOps Best Practices for Faster Software Delivery"},"content":{"rendered":"<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_80 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<label for=\"ez-toc-cssicon-toggle-item-69e7fe59e2f5e\" class=\"ez-toc-cssicon-toggle-label\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/label><input type=\"checkbox\"  id=\"ez-toc-cssicon-toggle-item-69e7fe59e2f5e\"  aria-label=\"Toggle\" \/><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#DevOps_Best_Practices_Why_Your_Pipeline_is_a_Liability\" >DevOps Best Practices: Why Your Pipeline is a Liability<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#The_Fallacy_of_the_%E2%80%9CLatest%E2%80%9D_Tag\" >The Fallacy of the &#8220;Latest&#8221; Tag<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#Infrastructure_as_Code_IaC_is_Not_Just_%E2%80%9CScripts_in_Git%E2%80%9D\" >Infrastructure as Code (IaC) is Not Just &#8220;Scripts in Git&#8221;<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#The_Alpine_vs_Debian-Slim_Debate\" >The Alpine vs. Debian-Slim Debate<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#CICD_The_%E2%80%9CContinuous%E2%80%9D_Part_is_a_Lie\" >CI\/CD: The &#8220;Continuous&#8221; Part is a Lie<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#Observability_Stop_Looking_at_Dashboards\" >Observability: Stop Looking at Dashboards<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#The_Database_Migration_Nightmare\" >The Database Migration Nightmare<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#Secret_Management_Base64_is_Not_Encryption\" >Secret Management: Base64 is Not Encryption<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#The_%E2%80%9CGotcha%E2%80%9D_The_Hidden_Cost_of_Managed_Services\" >The &#8220;Gotcha&#8221;: The Hidden Cost of Managed Services<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#The_Human_Element_On-Call_is_a_Feedback_Loop\" >The Human Element: On-Call is a Feedback Loop<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#YAML-Hell_and_the_Complexity_Trap\" >YAML-Hell and the Complexity Trap<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#Testing_the_Un-testable\" >Testing the Un-testable<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"DevOps_Best_Practices_Why_Your_Pipeline_is_a_Liability\"><\/span>DevOps Best Practices: Why Your Pipeline is a Liability<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>It was 3:14 AM on a Tuesday in 2018. I was staring at a terminal window, watching a Jenkins pipeline spin in a loop. We were migrating our core payment processing service to a new Kubernetes cluster in <code>us-east-1<\/code>. I had written a &#8220;clever&#8221; shell script that used <code>sed<\/code> to inject environment variables into a YAML manifest before running <code>kubectl apply<\/code>. I thought I was being efficient. I wasn&#8217;t. A malformed string caused the script to wipe the <code>spec.selector<\/code> field from the Deployment. Kubernetes, doing exactly what I told it to do, orphaned the existing pods and started spinning up new ones that couldn&#8217;t find their target. Traffic to <code>api.stripe.com<\/code> started failing. Our error rates hit 100%. The &#8220;clever&#8221; script had effectively deleted our production environment&#8217;s ability to route traffic.<\/p>\n<p>I spent the next four hours manually rebuilding the state while my manager breathed down my neck on a Zoom call. That night, I learned that &#8220;DevOps&#8221; isn&#8217;t about tools, scripts, or being clever. It\u2019s about building systems that are boring, predictable, and resistant to human stupidity. If your deployment process requires a &#8220;hero&#8221; to stay awake and watch the logs, you don&#8217;t have a DevOps culture; you have a hostage situation. Most <a href=\"https:\/\/itsupportwale.com\/blog\/\" title=\"Read more about blog\">blog<\/a> posts will tell you that DevOps is about &#8220;breaking down silos&#8221; or &#8220;accelerating delivery.&#8221; I\u2019m here to tell you that most <b>devops best<\/b> practices are actually about preventing you from setting your infrastructure on fire.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Fallacy_of_the_%E2%80%9CLatest%E2%80%9D_Tag\"><\/span>The Fallacy of the &#8220;Latest&#8221; Tag<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Stop using <code>:latest<\/code>. Just stop. It is the single most dangerous habit in container orchestration. When you pull <code>node:latest<\/code> or <code>python:3.9<\/code>, you are playing Russian Roulette with your build&#8217;s reproducibility. One morning, the maintainers push a patch that changes a shared library, and suddenly your production build fails because of a <code>glibc<\/code> mismatch that didn&#8217;t exist ten minutes ago.<\/p>\n<p>Immutability is the only way to maintain sanity. Every image you build should be tagged with a git commit SHA or a semantic version. Better yet, reference the image by its SHA-256 digest. This ensures that the bits you tested in staging are the exact same bits running in production. If you can&#8217;t guarantee that, your testing is a lie.<\/p>\n<ul>\n<li>Reference images by digest: <code>my-app@sha256:85755305246504ca827...<\/code><\/li>\n<li>Never use <code>imagePullPolicy: Always<\/code> in production unless you enjoy random outages during node restarts.<\/li>\n<\/ul>\n<pre><code># Bad Dockerfile\nFROM node:latest\nCOPY . .\nRUN npm install\nCMD [\"node\", \"index.js\"]\n\n# Better Dockerfile (Deterministic)\nFROM node:20.11.0-bookworm-slim@sha256:69396f866416629...\nWORKDIR \/app\nCOPY package.json package-lock.json .\/\nRUN npm ci --only=production\nCOPY src\/ .\/src\/\nUSER node\nCMD [\"node\", \"src\/index.js\"]\n<\/code><\/pre>\n<blockquote>\n<p><strong>Pro-tip:<\/strong> Use <code>npm ci<\/code> instead of <code>npm install<\/code> in your CI pipelines. It deletes the <code>node_modules<\/code> folder and installs the exact versions from your lockfile. It\u2019s faster and prevents &#8220;it works on my machine&#8221; syndrome.<\/p>\n<\/blockquote>\n<h2><span class=\"ez-toc-section\" id=\"Infrastructure_as_Code_IaC_is_Not_Just_%E2%80%9CScripts_in_Git%E2%80%9D\"><\/span>Infrastructure as Code (IaC) is Not Just &#8220;Scripts in Git&#8221;<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Most teams think they are doing IaC because they have some Terraform files in a repository. Then you look at their state file, and it\u2019s stored locally on a lead engineer&#8217;s laptop. Or worse, they have &#8220;drift&#8221;\u2014someone logged into the AWS console and manually changed a security group rule to &#8220;fix a quick issue,&#8221; and now the code doesn&#8217;t match reality. When you run <code>terraform plan<\/code>, it wants to delete the manual change, and you&#8217;re too scared to run <code>apply<\/code>.<\/p>\n<p>If you aren&#8217;t enforcing your infrastructure through a CI provider, you aren&#8217;t doing IaC. You&#8217;re doing &#8220;Manual Infrastructure with Extra Steps.&#8221; You need a remote state with locking. If two people try to run Terraform at the same time and you don&#8217;t have locking, you will corrupt your state file. I have seen a corrupted state file turn a 5-minute update into a 3-day recovery effort involving <code>terraform import<\/code> and a lot of crying.<\/p>\n<pre><code># terraform\/backend.tf\nterraform {\n  backend \"s3\" {\n    bucket         = \"my-company-terraform-state\"\n    key            = \"production\/network\/terraform.tfstate\"\n    region         = \"us-east-1\"\n    dynamodb_table = \"terraform-lock-table\"\n    encrypt        = true\n  }\n}\n<\/code><\/pre>\n<p>The <code>dynamodb_table<\/code> is not optional. It\u2019s the only thing standing between you and a race condition that nukes your VPC. Also, stop using <code>chmod 777<\/code> on everything because you&#8217;re frustrated with permissions. I once found a production S3 bucket with public read\/write access because a junior dev &#8220;couldn&#8217;t get the IAM policy to work.&#8221; Use the principle of least privilege. It\u2019s annoying, it\u2019s slow, but it\u2019s the only way to avoid being the lead story on Krebs on Security.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Alpine_vs_Debian-Slim_Debate\"><\/span>The Alpine vs. Debian-Slim Debate<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The hype cycle will tell you to use Alpine Linux for everything because it&#8217;s &#8220;small.&#8221; A 5MB base image sounds great until you realize it uses <code>musl<\/code> instead of <code>glibc<\/code>. If you are running Python, Ruby, or Node.js apps that rely on C extensions (like <code>pandas<\/code>, <code>bcrypt<\/code>, or <code>grpc<\/code>), you are going to have a bad time. You will spend hours debugging weird segmentation faults or watching your build times triple because you have to compile every dependency from source since there are no pre-built wheels for <code>musl<\/code>.<\/p>\n<p>I take a hard stand here: Use <code>debian-slim<\/code> (specifically the <code>bookworm<\/code> or <code>bullseye<\/code> variants). It\u2019s 30MB larger, but it\u2019s compatible with almost everything. Disk space is cheap; engineering time spent debugging <code>ldd<\/code> errors is expensive. Your <b>devops best<\/b> practices should prioritize stability over shaving 20MB off an image that\u2019s going to be cached on the node anyway.<\/p>\n<ul>\n<li>Alpine: Good for Go or Rust (statically linked binaries).<\/li>\n<li>Debian-Slim: Good for everything else.<\/li>\n<li>Distroless: Great for security, but a nightmare to debug when you need to <code>exec<\/code> into a pod to see why a config file is missing.<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"CICD_The_%E2%80%9CContinuous%E2%80%9D_Part_is_a_Lie\"><\/span>CI\/CD: The &#8220;Continuous&#8221; Part is a Lie<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We call it Continuous Deployment, but for most, it\u2019s &#8220;Continuous Anxiety.&#8221; A common mistake is building a pipeline that is too long. If your pipeline takes 45 minutes to run, developers will start batching commits. Batching commits makes it impossible to identify which change broke the build. Your CI should provide feedback in under 10 minutes. If it doesn&#8217;t, you need to parallelize your tests or fix your slow-ass Docker builds.<\/p>\n<p>One of the biggest <b>devops best<\/b> practices is the &#8220;Build Once, Deploy Many&#8221; rule. You should build your artifact (Docker image, JAR, binary) at the start of the pipeline. That same artifact should move through staging, UAT, and production. If you are rebuilding the code for each environment, you are not testing what you are deploying. You are testing a *copy* of what you are deploying. Subtle differences in build environments can and will break your app.<\/p>\n<pre><code># .github\/workflows\/deploy.yml\njobs:\n  build:\n    runs-on: ubuntu-latest\n    outputs:\n      image_tag: ${{ steps.vars.outputs.tag }}\n    steps:\n      - name: Build and Push\n        run: |\n          TAG=$(git rev-parse --short HEAD)\n          docker build -t my-reg\/app:$TAG .\n          docker push my-reg\/app:$TAG\n          echo \"tag=$TAG\" >> $GITHUB_OUTPUT\n\n  deploy-staging:\n    needs: build\n    environment: staging\n    runs-on: ubuntu-latest\n    steps:\n      - name: Update K8s\n        run: |\n          sed -i \"s|image:.*|image: my-reg\/app:${{ needs.build.outputs.image_tag }}|\" k8s\/deploy.yml\n          kubectl apply -f k8s\/deploy.yml\n<\/code><\/pre>\n<p>Note the use of <code>git rev-parse --short HEAD<\/code>. This links the deployment directly to a specific commit. If production goes down, I know exactly which lines of code are responsible. No guessing. No &#8220;I think it was the merge from yesterday.&#8221;<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Observability_Stop_Looking_at_Dashboards\"><\/span>Observability: Stop Looking at Dashboards<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Dashboards are for managers. Alerts are for engineers. If you have a dashboard with 50 widgets, you aren&#8217;t monitoring; you&#8217;re painting. You cannot look at 50 widgets during an incident. You need to define your SLIs (Service Level Indicators) and SLOs (Service Level Objectives). Focus on the &#8220;Four Golden Signals&#8221;: Latency, Traffic, Errors, and Saturation.<\/p>\n<p>High cardinality is the silent killer of Prometheus. If you start adding <code>user_id<\/code> or <code>order_id<\/code> as a label in your Prometheus metrics, you will blow up your TSDB (Time Series Database). Your Prometheus instance will start consuming 64GB of RAM and then OOM-kill itself right when you need it most. Keep your labels low-cardinality. Use logs or traces for high-cardinality data.<\/p>\n<blockquote>\n<p><strong>Note to self:<\/strong> Check the <code>prometheus_tsdb_head_series<\/code> metric. If it\u2019s climbing linearly, someone added a <code>uuid<\/code> label to a counter. Find them. Educate them. Or take away their keyboard.<\/p>\n<\/blockquote>\n<p>Real-world example of a useful Prometheus query for an SLO (99th percentile latency over 5 minutes):<\/p>\n<pre><code>histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{job=\"api-server\"}[5m]))) > 0.5\n<\/code><\/pre>\n<p>If this query returns a result, someone is getting paged. It\u2019s actionable. It\u2019s clear. It\u2019s not a &#8220;pretty graph&#8221; that no one looks at.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_Database_Migration_Nightmare\"><\/span>The Database Migration Nightmare<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Everyone forgets the database. You can roll back a container in 10 seconds. You cannot roll back a <code>DROP COLUMN<\/code> on a 5TB table in 10 seconds. DevOps best practices dictate that database migrations must be decoupled from code deployments. Your code should always be compatible with <code>N-1<\/code> version of the database schema.<\/p>\n<p>If you need to rename a column, it\u2019s a three-step process over two deployments:<\/p>\n<ol>\n<li>Add the new column, and write to both the old and new columns.<\/li>\n<li>Backfill the data from the old column to the new one.<\/li>\n<li>Update the code to read from the new column, then delete the old column in a separate migration weeks later.<\/li>\n<\/ol>\n<p>If you try to do this in one go, and the code deployment fails, you are stuck. You can&#8217;t roll back the code because the old code doesn&#8217;t know about the new schema. This is how data corruption happens. This is how you lose your job.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Secret_Management_Base64_is_Not_Encryption\"><\/span>Secret Management: Base64 is Not Encryption<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>I am still shocked by how many people think that Kubernetes Secrets are &#8220;secure.&#8221; They are just Base64 encoded strings. Anyone with <code>get secrets<\/code> access can read them. If you check your <code>.env<\/code> files into Git, you might as well post your AWS secret keys on Twitter. Use a real secret manager like HashiCorp Vault, AWS Secrets Manager, or at the very least, <code>sops<\/code> to encrypt your secrets at rest within your Git repo.<\/p>\n<pre><code># Example of using sops to encrypt a secret file\nsops --encrypt --gcp-kms projects\/my-project\/locations\/global\/keyRings\/my-ring\/cryptoKeys\/my-key secret.yaml > secret.enc.yaml\n<\/code><\/pre>\n<p>This allows you to keep your configuration in Git (GitOps) without exposing the sensitive bits. When the CI\/CD pipeline runs, it uses a service account with permission to decrypt the file. It\u2019s a bit more friction, but it prevents the &#8220;oops, I leaked the Stripe API key&#8221; post-mortem.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"The_%E2%80%9CGotcha%E2%80%9D_The_Hidden_Cost_of_Managed_Services\"><\/span>The &#8220;Gotcha&#8221;: The Hidden Cost of Managed Services<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Managed services (RDS, EKS, Managed Kafka) are great until they aren&#8217;t. People think &#8220;Managed&#8221; means &#8220;I don&#8217;t have to worry about it.&#8221; Wrong. Managed means &#8220;I don&#8217;t have to manage the hardware, but I still have to manage the configuration.&#8221; I once saw a team spend $20,000 in a single month because they enabled &#8220;Detailed Monitoring&#8221; on 500 CloudWatch metrics they never looked at. Or the time an &#8220;Auto-scaling&#8221; group scaled to 200 instances during a DDoS attack, costing a fortune because there was no upper limit set.<\/p>\n<p>You must set guardrails. Max instance counts, budget alerts, and TTLs (Time To Live) on experimental resources. In a cloud-native world, your <b>devops best<\/b> practices must include &#8220;Cloud Financial Management&#8221; (FinOps). If you don&#8217;t, your CFO will become your most frequent on-call page.<\/p>\n<ul>\n<li>Always set <code>max_size<\/code> on Auto Scaling Groups.<\/li>\n<li>Use <code>Taint<\/code> and <code>Tolerations<\/code> in K8s to keep expensive GPU workloads from running your simple web-cron jobs.<\/li>\n<li>Delete your unused EBS volumes. They are the &#8220;vampire power&#8221; of AWS.<\/li>\n<li>Set a 7-day retention policy on your non-production logs. You don&#8217;t need 2-year-old logs for a dev environment that doesn&#8217;t exist anymore.<\/li>\n<li>Audit your S3 storage classes. Moving old logs to Glacier can save 80% on storage costs.<\/li>\n<li>Use Spot instances for non-critical background jobs, but ensure your app can handle a <code>SIGTERM<\/code> gracefully.<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"The_Human_Element_On-Call_is_a_Feedback_Loop\"><\/span>The Human Element: On-Call is a Feedback Loop<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>If your developers aren&#8217;t on-call for the code they write, they will never write stable code. This is the core of DevOps. When a developer gets woken up at 2 AM because their new feature is throwing 500 errors, they become very motivated to write better tests and include better error handling. If the SRE team is the only one getting paged, the developers have no incentive to improve. They will keep throwing &#8220;features&#8221; over the wall, and the SREs will keep burning out.<\/p>\n<p>But on-call shouldn&#8217;t be a punishment. If a team is getting paged more than twice a week, the sprint should be stopped, and the next two weeks should be dedicated entirely to &#8220;Reliability Work.&#8221; No new features. Just fixing the technical debt that is causing the pages. This is how you build a sustainable culture. You cannot &#8220;DevOps&#8221; your way out of a toxic work environment that prioritizes velocity over stability.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"YAML-Hell_and_the_Complexity_Trap\"><\/span>YAML-Hell and the Complexity Trap<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We&#8217;ve traded &#8220;DLL Hell&#8221; for &#8220;YAML Hell.&#8221; Between Kubernetes manifests, Helm charts, and CI\/CD definitions, we are drowning in indentation-sensitive configuration. My advice? Keep it as flat as possible. Avoid deeply nested Helm charts with 500 lines of <code>values.yaml<\/code>. If you need a PhD to understand how a service is deployed, your abstraction is too leaky.<\/p>\n<p>I prefer Kustomize over Helm for internal apps. It\u2019s just plain YAML with overlays. No complex templating logic. No <code>{{ if .Values.global.enabled }}<\/code> blocks that make your eyes bleed. It\u2019s easier to debug and easier to audit. Remember: The goal of DevOps is to reduce cognitive load, not increase it.<\/p>\n<pre><code># kustomization.yaml\nresources:\n  - ..\/base\npatchesStrategicMerge:\n  - replica_count.yaml\n  - env_vars.yaml\n<\/code><\/pre>\n<p>It\u2019s simple. It\u2019s readable. It works.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Testing_the_Un-testable\"><\/span>Testing the Un-testable<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Unit tests are fine, but they won&#8217;t tell you if your IAM role has the right permissions to write to DynamoDB. For that, you need integration tests in a real environment. Tools like <code>LocalStack<\/code> are okay, but nothing beats a &#8220;Sandbox&#8221; AWS account where you can run <code>terraform apply<\/code> and run actual functional tests against real AWS APIs. Yes, it costs a few dollars. No, it\u2019s not as expensive as a production outage.<\/p>\n<p>And for the love of all that is holy, test your backups. A backup that hasn&#8217;t been restored is just a theoretical exercise. I\u2019ve seen companies lose weeks of data because they were &#8220;backing up&#8221; to a corrupted S3 bucket for months and never checked if the <code>tar<\/code> files were actually valid. Schedule a &#8220;Restoration Day&#8221; once a quarter. If you can&#8217;t bring your system up from scratch in a new region in under 4 hours, you don&#8217;t have a disaster recovery plan; you have a hope.<\/p>\n<p>DevOps is the practice of being relentlessly disciplined about the boring stuff. It\u2019s about pinning versions, locking state, limiting permissions, and actually reading the documentation before you copy-paste from StackOverflow. It\u2019s not flashy. It won&#8217;t get you a keynote at a conference. But it will let you sleep through the night. And in this industry, that is the only metric that matters.<\/p>\n<p>Stop chasing the hype and start fixing your <code>:latest<\/code> tags.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>DevOps Best Practices: Why Your Pipeline is a Liability It was 3:14 AM on a Tuesday in 2018. I was staring at a terminal window, watching a Jenkins pipeline spin in a loop. We were migrating our core payment processing service to a new Kubernetes cluster in us-east-1. I had written a &#8220;clever&#8221; shell script &#8230; <a title=\"10 DevOps Best Practices for Faster Software Delivery\" class=\"read-more\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/\" aria-label=\"Read more  on 10 DevOps Best Practices for Faster Software Delivery\">Read more<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-4767","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.0 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>10 DevOps Best Practices for Faster Software Delivery - ITSupportWale<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"10 DevOps Best Practices for Faster Software Delivery - ITSupportWale\" \/>\n<meta property=\"og:description\" content=\"DevOps Best Practices: Why Your Pipeline is a Liability It was 3:14 AM on a Tuesday in 2018. I was staring at a terminal window, watching a Jenkins pipeline spin in a loop. We were migrating our core payment processing service to a new Kubernetes cluster in us-east-1. I had written a &#8220;clever&#8221; shell script ... Read more\" \/>\n<meta property=\"og:url\" content=\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/\" \/>\n<meta property=\"og:site_name\" content=\"ITSupportWale\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-21T16:17:00+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"512\" \/>\n\t<meta property=\"og:image:height\" content=\"512\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Techie\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Techie\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/\"},\"author\":{\"name\":\"Techie\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\"},\"headline\":\"10 DevOps Best Practices for Faster Software Delivery\",\"datePublished\":\"2026-04-21T16:17:00+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/\"},\"wordCount\":2270,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/\",\"name\":\"10 DevOps Best Practices for Faster Software Delivery - ITSupportWale\",\"isPartOf\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\"},\"datePublished\":\"2026-04-21T16:17:00+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/itsupportwale.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"10 DevOps Best Practices for Faster Software Delivery\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#website\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"name\":\"ITSupportWale\",\"description\":\"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides\",\"publisher\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#organization\",\"name\":\"itsupportwale\",\"url\":\"https:\/\/itsupportwale.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"contentUrl\":\"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png\",\"width\":1119,\"height\":144,\"caption\":\"itsupportwale\"},\"image\":{\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/Itsupportwale-298547177495978\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d\",\"name\":\"Techie\",\"sameAs\":[\"https:\/\/itsupportwale.com\",\"iswblogadmin\"],\"url\":\"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"10 DevOps Best Practices for Faster Software Delivery - ITSupportWale","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/","og_locale":"en_US","og_type":"article","og_title":"10 DevOps Best Practices for Faster Software Delivery - ITSupportWale","og_description":"DevOps Best Practices: Why Your Pipeline is a Liability It was 3:14 AM on a Tuesday in 2018. I was staring at a terminal window, watching a Jenkins pipeline spin in a loop. We were migrating our core payment processing service to a new Kubernetes cluster in us-east-1. I had written a &#8220;clever&#8221; shell script ... Read more","og_url":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/","og_site_name":"ITSupportWale","article_publisher":"https:\/\/www.facebook.com\/Itsupportwale-298547177495978","article_published_time":"2026-04-21T16:17:00+00:00","og_image":[{"width":512,"height":512,"url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2021\/05\/android-chrome-512x512-1.png","type":"image\/png"}],"author":"Techie","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Techie","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#article","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/"},"author":{"name":"Techie","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d"},"headline":"10 DevOps Best Practices for Faster Software Delivery","datePublished":"2026-04-21T16:17:00+00:00","mainEntityOfPage":{"@id":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/"},"wordCount":2270,"commentCount":0,"publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/","url":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/","name":"10 DevOps Best Practices for Faster Software Delivery - ITSupportWale","isPartOf":{"@id":"https:\/\/itsupportwale.com\/blog\/#website"},"datePublished":"2026-04-21T16:17:00+00:00","breadcrumb":{"@id":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/itsupportwale.com\/blog\/10-devops-best-practices-for-faster-software-delivery-4\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/itsupportwale.com\/blog\/"},{"@type":"ListItem","position":2,"name":"10 DevOps Best Practices for Faster Software Delivery"}]},{"@type":"WebSite","@id":"https:\/\/itsupportwale.com\/blog\/#website","url":"https:\/\/itsupportwale.com\/blog\/","name":"ITSupportWale","description":"Tips, Tricks, Fixed-Errors, Tutorials &amp; Guides","publisher":{"@id":"https:\/\/itsupportwale.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/itsupportwale.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/itsupportwale.com\/blog\/#organization","name":"itsupportwale","url":"https:\/\/itsupportwale.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","contentUrl":"https:\/\/itsupportwale.com\/blog\/wp-content\/uploads\/2023\/09\/cropped-Logo-trans-without-slogan.png","width":1119,"height":144,"caption":"itsupportwale"},"image":{"@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/Itsupportwale-298547177495978"]},{"@type":"Person","@id":"https:\/\/itsupportwale.com\/blog\/#\/schema\/person\/8c5a2b3d36396e0a8fd91ec8242fd46d","name":"Techie","sameAs":["https:\/\/itsupportwale.com","iswblogadmin"],"url":"https:\/\/itsupportwale.com\/blog\/author\/iswblogadmin\/"}]}},"_links":{"self":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4767","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/comments?post=4767"}],"version-history":[{"count":0,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/posts\/4767\/revisions"}],"wp:attachment":[{"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/media?parent=4767"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/categories?post=4767"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itsupportwale.com\/blog\/wp-json\/wp\/v2\/tags?post=4767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}