<?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>Forem: Rue Matchaba</title>
    <description>The latest articles on Forem by Rue Matchaba (@ruethedev).</description>
    <link>https://forem.com/ruethedev</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%2F3825208%2F42f0372e-65eb-4b82-9322-ee922f062ea7.png</url>
      <title>Forem: Rue Matchaba</title>
      <link>https://forem.com/ruethedev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ruethedev"/>
    <language>en</language>
    <item>
      <title>How I Cut My Cloud SQL Bill by 70% After Discovering 0.8% CPU Utilization</title>
      <dc:creator>Rue Matchaba</dc:creator>
      <pubDate>Thu, 30 Apr 2026 00:06:38 +0000</pubDate>
      <link>https://forem.com/ruethedev/how-i-cut-my-cloud-sql-bill-by-70-after-discovering-08-cpu-utilization-415g</link>
      <guid>https://forem.com/ruethedev/how-i-cut-my-cloud-sql-bill-by-70-after-discovering-08-cpu-utilization-415g</guid>
      <description>&lt;h2&gt;
  
  
  The Cloud SQL Bill That Taught Me Everything About Over-Provisioning
&lt;/h2&gt;

&lt;p&gt;My database was running at 0.8% CPU utilisation.&lt;/p&gt;

&lt;p&gt;I discovered this three months after going live, while investigating why our GCP bill seemed higher than expected for our traffic volume. The number was so low I thought there was an error in Cloud Monitoring. There wasn't.&lt;/p&gt;

&lt;p&gt;I'd been paying for a machine that could handle roughly 100x more load than we were actually putting on it. Classic over-provisioning, but seeing it in real numbers was genuinely embarrassing.&lt;/p&gt;

&lt;p&gt;Here's everything I learned about right-sizing Cloud SQL instances, with the specific metrics and commands that will save you from making the same mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Cost of "Playing It Safe"
&lt;/h2&gt;

&lt;p&gt;When you're spinning up your first production Cloud SQL instance, the console gives you a dropdown of machine types: &lt;code&gt;db-f1-micro&lt;/code&gt;, &lt;code&gt;db-n1-standard-1&lt;/code&gt;, &lt;code&gt;db-n1-standard-2&lt;/code&gt;, and so on. The descriptions are helpful but vague: "1 vCPU, 3.75GB memory" tells you the specs, not whether you need them.&lt;/p&gt;

&lt;p&gt;I picked &lt;code&gt;db-n1-standard-2&lt;/code&gt; because it seemed reasonable for a production database. Not too small, not excessive. The middle option. That decision was based on absolutely no data.&lt;/p&gt;

&lt;p&gt;The problem with "reasonable" is that it's usually wrong. Either you're under-provisioned and your app breaks, or you're over-provisioned and you're burning money. In my case, it was the latter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Metrics Actually Tell You
&lt;/h2&gt;

&lt;p&gt;The key insight is that Cloud Monitoring shows you exactly what your database is doing. You just have to know where to look.&lt;/p&gt;

&lt;h3&gt;
  
  
  CPU Utilisation
&lt;/h3&gt;

&lt;p&gt;This is the most important metric for right-sizing your instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where to find it:&lt;/strong&gt; Cloud Console → SQL → your instance → Monitoring tab → CPU utilization&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to look for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Average utilisation over the past 30 days&lt;/li&gt;
&lt;li&gt;P95 and P99 peaks (the highest 5% and 1% of usage)&lt;/li&gt;
&lt;li&gt;Time of day patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to interpret it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Under 20% average: you can probably downgrade&lt;/li&gt;
&lt;li&gt;20-50%: you're sized appropriately&lt;/li&gt;
&lt;li&gt;50-80%: keep an eye on growth trends&lt;/li&gt;
&lt;li&gt;Over 80% sustained: consider upgrading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My average was 0.8%. My P99 was around 3%. I could have run the same workload on a &lt;code&gt;db-f1-micro&lt;/code&gt; instance and saved roughly 70% on compute costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Utilisation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Where to find it:&lt;/strong&gt; Same monitoring tab → Memory utilization&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What matters:&lt;/strong&gt; You want to see consistent memory usage without swap. If memory utilisation is consistently above 90% or you're seeing any swap usage, that's a performance problem waiting to happen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I found:&lt;/strong&gt; Memory usage was sitting around 15% with zero swap. Another sign I was massively over-provisioned.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection Count
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Where to find it:&lt;/strong&gt; Monitoring tab → Database connections&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to look for:&lt;/strong&gt; Peak active connections compared to your instance's connection limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection limits by instance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;db-f1-micro&lt;/code&gt;: 25 connections&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db-n1-standard-1&lt;/code&gt;: 100 connections&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db-n1-standard-2&lt;/code&gt;: 200 connections&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My peak connections were hitting around 11. Even a &lt;code&gt;db-f1-micro&lt;/code&gt; would have been comfortable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Commands That Actually Matter
&lt;/h2&gt;

&lt;p&gt;Once you know your utilisation is low, here are the specific commands to check what you're currently running and how to change it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check Your Current Instance Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud sql instances describe YOUR_INSTANCE_NAME &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"table(
    name,
    settings.tier,
    settings.dataDiskSizeGb,
    settings.availabilityType,
    settings.backupConfiguration.enabled
)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a clean summary of what you're paying for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;settings.tier&lt;/code&gt;: your machine type (the expensive part)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;settings.dataDiskSizeGb&lt;/code&gt;: disk size&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;settings.availabilityType&lt;/code&gt;: whether HA is enabled&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;settings.backupConfiguration.enabled&lt;/code&gt;: backup settings&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Downgrade Your Instance Tier
&lt;/h3&gt;

&lt;p&gt;If your CPU utilisation is consistently low, this is the biggest cost saving:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud sql instances patch YOUR_INSTANCE_NAME &lt;span class="nt"&gt;--tier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;db-f1-micro
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; This will restart your instance. Plan for a few minutes of downtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Machine type costs&lt;/strong&gt; (rough monthly estimates for PostgreSQL in us-central1):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;db-f1-micro&lt;/code&gt;: ~$7/month&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db-n1-standard-1&lt;/code&gt;: ~$25/month&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db-n1-standard-2&lt;/code&gt;: ~$50/month&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db-n1-standard-4&lt;/code&gt;: ~$100/month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Moving from standard-2 to f1-micro saves around $43/month per instance. That adds up fast if you're running multiple environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Turn Off High Availability (Where Appropriate)
&lt;/h3&gt;

&lt;p&gt;High Availability runs a standby replica in a different zone, roughly doubling your instance cost. You want this in production. You probably don't need it in staging or development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check if HA is enabled:&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;gcloud sql instances describe YOUR_INSTANCE_NAME &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"value(settings.availabilityType)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Turn it off:&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;gcloud sql instances patch YOUR_INSTANCE_NAME &lt;span class="nt"&gt;--availability-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ZONAL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Turn it back on:&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;gcloud sql instances patch YOUR_INSTANCE_NAME &lt;span class="nt"&gt;--availability-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;REGIONAL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This change also requires a restart, so plan accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Storage Problem You Can't Fix Easily
&lt;/h2&gt;

&lt;p&gt;Here's the frustrating part: Cloud SQL storage auto-increases but never auto-decreases. If your data grows to 50GB and then you delete 40GB, you're still paying for 50GB forever.&lt;/p&gt;

&lt;p&gt;I had 100GB provisioned and was using 240MB. That's 0.24% utilisation. Storage isn't the most expensive part of Cloud SQL, but it's still $10/month I didn't need to spend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check your actual storage usage:&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;gcloud sql instances describe YOUR_INSTANCE_NAME &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"value(settings.dataDiskSizeGb)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then connect to your database and check actual usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- For PostgreSQL&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_database_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'your_database_name'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;-- For MySQL&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; 
    &lt;span class="n"&gt;table_schema&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nv"&gt;"Database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;ROUND&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;index_length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="nv"&gt;"Size (MB)"&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tables&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;table_schema&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;The only fix for oversized storage:&lt;/strong&gt; export your data, delete the instance, and recreate it with a smaller disk. This is disruptive enough that you probably won't do it unless the over-provisioning is severe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; Size your initial disk conservatively. 10GB is the minimum and sufficient for most applications starting out. You can always increase it later without downtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Backup Configuration That Actually Makes Sense
&lt;/h2&gt;

&lt;p&gt;Cloud SQL defaults to 7 days of automated backup retention. For production, that makes sense. For staging environments that get refreshed weekly, you're paying to store backups of data you'd never restore.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Check your backup settings:&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;gcloud sql instances describe YOUR_INSTANCE_NAME &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"table(
    settings.backupConfiguration.enabled,
    settings.backupConfiguration.retainedBackups,
    settings.backupConfiguration.pointInTimeRecoveryEnabled
)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reduce backup retention for non-critical instances:&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;gcloud sql instances patch YOUR_INSTANCE_NAME &lt;span class="nt"&gt;--backup-retain-count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Turn off point-in-time recovery (PITR) for non-critical instances:&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;gcloud sql instances patch YOUR_INSTANCE_NAME &lt;span class="nt"&gt;--no-backup-point-in-time-recovery&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PITR keeps transaction logs to allow recovery to any specific timestamp. It's useful for production but adds storage costs and complexity for environments where you'd just restore from the most recent daily backup anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Monitoring Dashboard You Should Actually Use
&lt;/h2&gt;

&lt;p&gt;Instead of checking individual metrics manually, set up a custom dashboard that shows everything relevant at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a monitoring workspace&lt;/strong&gt; (if you don't have one):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud alpha monitoring dashboards create &lt;span class="nt"&gt;--config-from-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dashboard-config.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dashboard configuration&lt;/strong&gt; (&lt;code&gt;dashboard-config.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;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Cloud&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SQL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Cost&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Optimization"&lt;/span&gt;
&lt;span class="na"&gt;mosaicLayout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;tiles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
    &lt;span class="na"&gt;widget&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CPU&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Utilization"&lt;/span&gt;
      &lt;span class="na"&gt;xyChart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;dataSets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;timeSeriesQuery&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;timeSeriesFilter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resource.type="cloudsql_database"'&lt;/span&gt;
              &lt;span class="na"&gt;metricFilter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metric.type="cloudsql.googleapis.com/database/cpu/utilization"'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
    &lt;span class="na"&gt;widget&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Memory&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Utilization"&lt;/span&gt;
      &lt;span class="na"&gt;xyChart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;dataSets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;timeSeriesQuery&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;timeSeriesFilter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resource.type="cloudsql_database"'&lt;/span&gt;
              &lt;span class="na"&gt;metricFilter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metric.type="cloudsql.googleapis.com/database/memory/utilization"'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;
    &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
    &lt;span class="na"&gt;widget&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Active&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Connections"&lt;/span&gt;
      &lt;span class="na"&gt;xyChart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;dataSets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;timeSeriesQuery&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;timeSeriesFilter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;resource.type="cloudsql_database"'&lt;/span&gt;
              &lt;span class="na"&gt;metricFilter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;metric.type="cloudsql.googleapis.com/database/postgresql/num_backends"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a single view of the three metrics that matter most for cost optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Wish I'd Known Before Clicking "Create"
&lt;/h2&gt;

&lt;p&gt;The real lesson here isn't about any specific setting. It's about the mindset.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start smaller than you think you need.&lt;/strong&gt; Scaling up is a one-line command and a few minutes of downtime. Scaling down requires migration and planning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use actual data, not gut feel.&lt;/strong&gt; Cloud Monitoring exists for a reason. If you don't have usage patterns yet, start with the smallest instance that can handle your expected load and scale up based on real metrics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Environment-specific configuration matters.&lt;/strong&gt; Production and staging have different availability requirements, different backup needs, and different cost tolerances. Configure them differently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GCP defaults optimize for reliability, not cost.&lt;/strong&gt; That's the right choice for a platform, but it means you need to actively optimize for your actual usage patterns.&lt;/p&gt;

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

&lt;p&gt;My 0.8% CPU utilisation was embarrassing, but it taught me more about cloud cost optimization than months of reading best practices guides. The specific numbers forced me to understand what each metric actually means and how it translates to real money.&lt;/p&gt;

&lt;p&gt;If you're setting up Cloud SQL for the first time, open the monitoring dashboard before you pick your instance tier. The metrics will tell you what you actually need, not what feels reasonable.&lt;/p&gt;

&lt;p&gt;And if you're already running Cloud SQL instances, spend ten minutes checking your utilisation numbers. You might be surprised at what you find.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>devops</category>
      <category>softwareengineering</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Was Hand-Writing Every AI Tool. Then I Discovered MCP Servers.</title>
      <dc:creator>Rue Matchaba</dc:creator>
      <pubDate>Fri, 27 Mar 2026 20:58:47 +0000</pubDate>
      <link>https://forem.com/ruethedev/i-was-hand-writing-every-ai-tool-then-i-discovered-mcp-servers-56np</link>
      <guid>https://forem.com/ruethedev/i-was-hand-writing-every-ai-tool-then-i-discovered-mcp-servers-56np</guid>
      <description>&lt;p&gt;&lt;em&gt;What tool calling and MCP actually mean, and how they fit together when you're building real AI products.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I've been building Pulse, a voice AI co-pilot for engineering work that talks to Jira and GitHub. The idea is simple: speak a command, Claude figures out what to do, your project management tools respond.&lt;/p&gt;

&lt;p&gt;To make it work, I had to give Claude the ability to interact with Jira and GitHub. So I did what most people do when they start building with LLMs: I wrote the tools by hand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;tools&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;create_jira_ticket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&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;, input_schema: { ... } },&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;get_jira_issue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&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;, input_schema: { ... } },&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;update_jira_status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&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;, input_schema: { ... } },&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three tools. Done. It worked fine.&lt;/p&gt;

&lt;p&gt;Then I learned what an MCP server actually is, and I realised I had been building with a teaspoon when a fire hose was sitting right there.&lt;/p&gt;




&lt;h2&gt;
  
  
  First, what is tool calling?
&lt;/h2&gt;

&lt;p&gt;When you build an LLM application, the model lives in a box. It can think, reason, and generate text, but it cannot &lt;em&gt;do&lt;/em&gt; anything in the real world on its own.&lt;/p&gt;

&lt;p&gt;Tool calling is how you fix that. You define a set of functions and tell the model they exist. When the model decides one is needed, it calls it with the right arguments. Your code executes the function and passes the result back to the model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User gives instruction
      ↓
Claude decides which tool to call
      ↓
Your code executes it
      ↓
Claude gets the result and responds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's powerful. But you write every tool yourself. You define the schema, you maintain it, you add new ones when you need them.&lt;/p&gt;




&lt;h2&gt;
  
  
  So what is an MCP server?
&lt;/h2&gt;

&lt;p&gt;MCP stands for Model Context Protocol. The simplest way I can explain it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool calling is giving Claude a telephone. MCP is giving Claude a universal remote control.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The telephone analogy works because with hand-rolled tools, you're wiring up each number yourself. You decide what Claude can call, you write the definition, you maintain it forever.&lt;/p&gt;

&lt;p&gt;An MCP server is a running process that speaks a standard protocol. Claude connects to it and asks: &lt;em&gt;"what tools do you have?"&lt;/em&gt; The server responds with a full list. Claude now knows everything it can do without you having written a single tool definition.&lt;/p&gt;

&lt;p&gt;More concretely:&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;Tool Calling&lt;/th&gt;
&lt;th&gt;MCP Server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What it is&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Functions you define manually&lt;/td&gt;
&lt;td&gt;A running server Claude connects to&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You write each function and schema&lt;/td&gt;
&lt;td&gt;Server exposes its tools automatically&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reusability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tied to your app&lt;/td&gt;
&lt;td&gt;Any AI that speaks MCP can use it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yours forever&lt;/td&gt;
&lt;td&gt;The server owner's problem&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Jira has an MCP server. GitHub has an MCP server. Instead of my 3 hand-rolled tools, I can point Pulse at both servers and Claude automatically gets access to the full API surface of each product.&lt;/p&gt;

&lt;p&gt;Sprints. PRs. Worklogs. Reviews. Branches. Comments. Issue history.&lt;/p&gt;

&lt;p&gt;Same voice interface. Dramatically larger capability surface.&lt;/p&gt;




&lt;h2&gt;
  
  
  The part that actually blew my mind
&lt;/h2&gt;

&lt;p&gt;Claude can reason across multiple MCP servers simultaneously.&lt;/p&gt;

&lt;p&gt;So instead of three isolated tool calls, you can say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Find all Jira tickets marked Done this sprint, check if the linked PR was actually merged, and flag any that say Done but the PR is still open."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That used to be a custom script someone had to write, test, and maintain. With MCP, it's a voice command.&lt;/p&gt;

&lt;p&gt;And when Jira ships new API features? The MCP server updates. Claude automatically has access. You change nothing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The broader point
&lt;/h2&gt;

&lt;p&gt;Most people are still thinking about AI as a chatbot. You ask it something, it answers.&lt;/p&gt;

&lt;p&gt;What I've been building toward is composing intelligence over systems. Claude isn't just answering questions. It's reasoning across your Jira board, your GitHub history, and your internal data, then taking action.&lt;/p&gt;

&lt;p&gt;The tools that make that possible are not complicated individually. What takes work is understanding how they fit together and when to use each one.&lt;/p&gt;

&lt;p&gt;That's what I'm figuring out in public, one project at a time.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm Rue, a Full Stack and AI Engineer building Pulse and writing about what I learn along the way. Follow along on Instagram at &lt;a href="https://instagram.com/rue.on.ai" rel="noopener noreferrer"&gt;@rue.on.ai&lt;/a&gt; or connect with me on LinkedIn.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I built my first AI agent. It was mostly plumbing</title>
      <dc:creator>Rue Matchaba</dc:creator>
      <pubDate>Fri, 27 Mar 2026 20:44:55 +0000</pubDate>
      <link>https://forem.com/ruethedev/i-built-my-first-ai-agent-it-was-mostly-plumbing-foj</link>
      <guid>https://forem.com/ruethedev/i-built-my-first-ai-agent-it-was-mostly-plumbing-foj</guid>
      <description>&lt;p&gt;I spent a weekend trying to understand how AI agents actually work. Not the pitch deck version. The code version. What does function calling look like in practice? What happens when two agents are chained together and one of them fails?&lt;/p&gt;

&lt;p&gt;I built a multi-agent research assistant in TypeScript to find out. Three agents: an orchestrator, a summarizer, and a writer. Each one does one job and hands off to the next.&lt;/p&gt;

&lt;p&gt;Running a model locally is weirder than it sounds.&lt;/p&gt;

&lt;p&gt;I used Ollama, which runs the model directly on your machine. No API key, no remote server. The first time my Express app got a real response back from localhost, I sat there for a second. It’s just an HTTP call. Your code genuinely cannot tell whether there’s a llama3.2 process on your laptop behind it or a data centre somewhere.&lt;/p&gt;

&lt;p&gt;Nobody told me that LLMs are just APIs. Text in, text out. Everything interesting happens inside that call, invisible to your code.&lt;/p&gt;

&lt;p&gt;Function calling is less magic than I expected, which was both a relief and slightly disappointing.&lt;/p&gt;

&lt;p&gt;You describe available tools in the system prompt. The model decides whether to use one and returns structured output with a tool name and arguments. Your code runs the tool and feeds the result back. That’s the whole loop.&lt;/p&gt;

&lt;p&gt;I kept waiting for something more mysterious. It’s just prompt engineering and plumbing.&lt;/p&gt;

&lt;p&gt;The chaining part was mostly plumbing.&lt;/p&gt;

&lt;p&gt;Agent 1 runs, its output becomes input for Agent 2, repeat. What actually took time was writing each agent’s system prompt tight enough that it wouldn’t drift into doing something adjacent to its job, and handling failures mid-chain without the whole thing collapsing silently.&lt;/p&gt;

&lt;p&gt;I wrote more error handling code than AI code. I think that’s correct.&lt;/p&gt;

&lt;p&gt;If I did it again.&lt;/p&gt;

&lt;p&gt;llama3.2 via Ollama is free and runs locally, but it gets shaky on complex instruction-following. If your pipeline depends on consistent structured output, that inconsistency compounds across three agents fast. A hosted model with real function calling support would have saved me some debugging time.&lt;/p&gt;

&lt;p&gt;I also didn’t document anything while I was building. By the time I was done, I’d forgotten what actually confused me at the start. Writing this post took longer than it should have for that reason. If you want to understand agents without paying for API calls, Ollama gets you there. Just design around what the model can’t reliably do. Super excited to see what l can build.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
