How Bytebase ships campaign changes in minutes, not days.

Bytebase

Bytebase is an open-source database DevSecOps platform — schema migration and access management for engineering teams. It reaches most of its customers through search, so it moved its Google Ads account into Adjar config and put Claude Code to work — Ryan Lockhart, its growth team of one, now ships campaign changes as reviewable diffs instead of console clicks.

Industry
Developer tools
Ad platform
Google Ads
AI agent
Claude Code
8x

More campaign tuning cycles shipped per month.

32%

Cost saving from an optimized ads strategy — same performance.

1 config

All campaigns codified as version-controlled, agent-readable text.

The challenge

Bytebase runs paid search with a growth team of one. Ryan Lockhart owns every campaign, keyword, and creative — alongside the rest of the growth function — while a steady stream of product launches keeps the ads needing updates. Every change meant clicking through the Google Ads console, one screen at a time.

Most of the company already runs with AI agents in the loop — coding, customer support, sales projections. Ads were the exception: the console is built for human clicks, so an agent couldn't help. Changes queued up behind whatever else demanded Ryan's week, and a round of tuning could stretch across days.

The solution

With Adjar, Bytebase exported its entire Google Ads account into declarative config in the website repo. Every budget, bid strategy, keyword, and headline now lives in text an agent can read and a human can review — a root file composes shared assets, conversion values, and one file per campaign.

.claude/skills/ads/
├── SKILL.md             # teaches the agent the workflow
├── config/
│   ├── google.toml      # account root — composes the rest
│   └── google/
│       ├── assets.toml       # shared sitelinks & callouts
│       ├── conversions.toml  # conversion actions & values
│       └── campaigns/
│           ├── db-change-mgmt.toml
│           ├── db-change-mgmt-jp.toml
│           └── db-access-control.toml
└── reports/             # perf pulled from Google Ads daily
    ├── 2026-04.google.md  # totals, daily, conversions
    ├── 2026-05.google.md
    └── 2026-06.google.md
Skill directory layout

Inside a campaign file, the whole funnel is explicit — spend caps, targeting, negative keywords, and creative — in plain TOML an agent can read and edit:

config/google/campaigns/db-access-control.toml
[[campaigns]]
name = "DB Access Control | SEARCH | 2026-Q2"
status = "ENABLED"
channel_type = "SEARCH"
bid_strategy = "TARGET_SPEND"
max_cpc_ceiling_usd = 10
daily_budget_usd = 200
sitelinks = [ "sl-pricing", "sl-data-masking" ]

[campaigns.targeting]
include_geos = [ "US" ]
include_languages = [ "en" ]
include_devices = [ "DESKTOP" ]

[[campaigns.negative_keywords]]
text = "cybersecurity"
match_type = "BROAD"

[[campaigns.ad_groups]]
name = "JIT access keywords"
status = "ENABLED"

[[campaigns.ad_groups.keywords]]
text = "just in time database access"
match_type = "PHRASE"

[[campaigns.ad_groups.ads]]
name = "Just-in-Time DB Access"
type = "RESPONSIVE_SEARCH_AD"
final_urls = [
  "https://www.bytebase.com/database-access-control/?utm_…",
]

[[campaigns.ad_groups.ads.headlines]]
text = "Just-in-Time DB Access"

[[campaigns.ad_groups.ads.descriptions]]
text = "Grant auto-expiring DB access with approvals & audit."

The directory doubles as a Claude Code Skill. A SKILL.md at its root teaches the agent the workflow — where the config lives, how to propose and apply a change, what the guardrails are — so every session starts with full context, no prompt engineering. Ryan invokes the skill and asks.

# bootstrap to file
adjar import --account <id> -o config/google.toml

# diff TOML vs platform; print plan (read-only)
adjar plan --config config/google.toml

# fresh diff → confirm → execute → write ids back
adjar apply --config config/google.toml
adjar CLI

Now ads work like the rest of the company's agent-driven work. Ryan tells Claude Code what he wants in plain language — launch a campaign, rebalance budgets, cut a keyword that's burning spend. The agent edits the config; Adjar turns the diff into a reviewable plan, flags anything that would reset learning, and applies it once he approves.

We define all our ads in a config and let Claude Code work with it — instead of digging through the ad console. Campaign changes and tuning that took days now ship in minutes.

Ryan Lockhart

Lead Growth Marketer, Bytebase

Adjar closes the loop with reporting. Fresh Google Ads performance lands in the repo daily as plain markdown, right next to the config. Claude Code reads both: it spots the keyword that spends without converting, the campaign whose CTR is sliding, the search terms worth promoting to exact match — and proposes the fix in the same session. Analysis and action, one workflow.

reports/2026-06.google.md
## Daily breakdown

### 2026-06-02

| Campaign            | Clicks |   CTR |    Cost | Conv. |
| ------------------- | -----: | ----: | ------: | ----: |
| DB Change Mgmt      |     38 | 3.16% |  $69.92 |     2 |
| DB Change Mgmt · JP |      6 | 1.94% |   $5.76 |     0 |
| DB Access Control   |     52 | 1.81% |  $73.84 |     1 |
| Subtotal            |     96 | 2.19% | $149.52 |     3 |

In practice it looks like this: reading the report, Claude Code noticed an ad group paying for CyberArk support queries — people looking for CyberArk's own docs, not an alternative — and proposed the fix itself:

⏺ The JIT access group is catching CyberArk support queries —
  "JIT access" is a CyberArk feature too. Adding an ad-group
  negative to filter them:

⏺ Update(config/google/campaigns/db-access-control.toml)
  ⎿ Added 4 lines
     23   [[campaigns.ad_groups.keywords]]
     24   text = "just in time database access"
     25   match_type = "PHRASE"
     26 +
     27 + [[campaigns.ad_groups.negative_keywords]]
     28 + text = "cyberark"
     29 + match_type = "BROAD"
     30
     31   [[campaigns.ad_groups.ads]]
Claude Code session

The results

The speed is the obvious win. A campaign change that used to wait for a free stretch in the console now ships in minutes: Ryan describes it in a sentence, reads the diff, and applies it. Bytebase runs about eight times as many tuning rounds a month as it did before.

That steady tuning shows up in the bill. Ad spend is down 32% with performance unchanged, and most of the savings came from waste that used to sit unnoticed for weeks — keywords spending without converting, queries from people who were never going to buy — and now gets caught in the daily report and cut the same day.

The program still runs on one person. Claude Code does the reading and the drafting; Ryan does the judgment. And because every change is a commit, the account has what the console never gave him: an author on every change, a review before it goes live, and a way back when an experiment doesn't pay off.

Ads used to be the one corner of the company AI agents couldn't reach. Adjar removed that exception: the ad account is now config an agent can read, edit, and optimize — and paid search became one more thing Bytebase ships through review.

Run your ads the way Bytebase does.

Book a demo — we’ll get your ad account into config and your agents working.

Book a demo