Register your interest: Tag @Cody, get an agent
BlogEngineering

Neon and CodeWords: branching a database like code

Scale to zero and the cold start it implies, branches that copy on write, connection pooling that is not optional, and automating an ephemeral database per pull request.

Aymeric ZhuoAymeric Zhuo11 min read

Summarize with AI

Neon and CodeWords: branching a database like code
On this page

Neon is Postgres with storage and compute separated, and two consequences follow from that architecture. Compute can scale to zero when nothing is connected, and a branch of the database can be created almost instantly because it shares storage with its parent until something writes.

The second is the interesting one for automation. A database branch per pull request, with production-shaped data, created and destroyed automatically, is a genuinely different way to test — and it is a scheduled API call rather than an infrastructure project.

What we'll cover

Branching, and what it actually copies

A branch shares its parent's storage and diverges only where something writes, which is why creating one is fast regardless of database size.

It is a full Postgres database, not a snapshot or a read replica — you can write to it freely.

Branches can be created from a point in time within the retention window, which is what makes recovery from a bad migration a branch rather than a restore.

Each branch has its own compute, so a branch that nobody is using costs little once it scales to zero.

Branches accumulate. Created per pull request and never deleted, they become a quiet cost and a quiet governance problem, which is the argument for automating the cleanup at the same time as the creation.

Production data on a branch is still production data. A branch for testing carries whatever the parent held, so anonymisation is a decision to make rather than a step to skip.

Scale to zero and its cold start

Compute suspends when nothing is connected, which is where the cost saving comes from.

Resuming takes a moment, and an automation with a very short connection timeout will treat a normal resume as a failure.

The first query after a resume is slower, which matters for anything measuring latency.

A scheduled automation is exactly the pattern that meets this, since it connects infrequently by definition.

Configure the suspend delay deliberately. Aggressive suspension saves more and produces more cold starts; the right setting depends on how often you connect.

Production with steady traffic rarely suspends, so this is mostly a consideration for branches, development environments, and scheduled work.

What Neon reaches

The Postgres wire protocol, so every ordinary Postgres capability and client applies — this is real Postgres, not an approximation.

Projects, branches, and endpoints can be created, listed, and deleted through the management API, which is what the automation here uses.

Point-in-time branch creation within the retention window configured for the project.

Connection strings can be retrieved per branch, including the pooled variant.

Roles and databases can be managed through the API.

Consumption metrics report compute and storage use, which is the basis of cost reporting.

A serverless driver over HTTP exists for environments where a persistent connection is impractical, which suits short-lived functions.

Connecting it to CodeWords

CodeWords connects to more than 3,000 integrations, and the connection is made once and reused.

  1. Open CodeWords and start a new automation.
  2. Describe what should happen in plain language to Cody, the automation builder: which project or branch, and what should happen.
  3. Authorize the connection with an API key for the management operations, and a pooled connection string for anything querying.
  4. Describe the exceptions: a branch that already exists, a compute still resuming, a branch older than your retention policy.
  5. Test against a scratch project before anything touches production branches.

You describe the outcome; Cody builds it, connects it, and deploys it. The free plan covers light use, with Pro at $39 per month and Business at $100 per month as usage grows; details are on the pricing page.

Seven automations worth building

Branch per pull request. Created when the branch opens, deleted when it merges or closes. Covered below, and it is the clearest reason to be on Neon at all.

Branch cleanup. Branches older than a threshold, or whose pull request is long closed, removed on a schedule. Without this, the first automation becomes a cost problem.

Cost and consumption reporting. Compute hours and storage by branch and project, since branches make it easy to accumulate spend nobody attributed.

Data anonymisation on branch creation. A development branch with production shape and no personal data, which is what makes production-like testing acceptable.

Migration rehearsal. Create a branch, run the migration, report how long it took and what it locked, before it runs anywhere real.

Recovery branches on incident. When something goes wrong, a branch from just before it becomes an investigation environment without touching production.

Standard operational reporting. The data quality checks, reconciliations, and scheduled reports you would run against any Postgres, against a branch rather than the production compute.

A database per pull request

The workflow worth building, described concretely.

Create the branch when the pull request opens, named after it so the correspondence is obvious.

Anonymise if it carries production data. A test environment holding real customer records is the kind of thing that is fine until it is not.

Publish the connection string to the environment that runs the preview, so the application deploys against its own database.

Run migrations against it as part of the pipeline, which means every pull request exercises the migration rather than discovering it at release.

Delete it when the pull request closes, and sweep for orphans weekly, because a webhook will eventually be missed.

Report what exists. A list of live branches with their age and their pull request makes the cleanup verifiable rather than assumed.

The result is that every change is tested against production-shaped data, and the migration runs dozens of times before it runs once where it matters.

Connections, which need pooling

Standard Postgres advice, and it applies more sharply here.

Use the pooled connection string for anything with concurrency, which is the default recommendation rather than an optimisation.

Know the pooling mode's constraints. Transaction pooling breaks session state, including prepared statement handling in some drivers and anything relying on LISTEN.

Use the direct connection string for migrations and anything needing session-level features.

Close what you open, since connections held open also keep compute from suspending, which quietly removes the scale-to-zero saving.

Short-lived functions suit the HTTP driver, which avoids the connection lifecycle entirely for simple queries.

Anonymising a branch properly

If branches carry production data into testing, the anonymisation step decides whether that is acceptable, and doing it badly is worse than not claiming to do it.

Replace, do not mask. A masked email that still maps one-to-one to a person is still personal data in most readings.

Keep the shape. Names should look like names and addresses like addresses, or the branch stops being useful for the testing it exists for.

Preserve referential integrity. The same customer must anonymise to the same value everywhere, or joins break and the environment misleads.

Handle free text. Support notes and comment fields contain personal information that no column name suggests, and they are the part most often missed.

Do it on the branch, immediately after creation, before anybody connects, and fail the branch creation if it does not complete.

Record that it ran. A branch that was supposed to be anonymised and quietly was not is the worst of both outcomes, and only a recorded result distinguishes them.

Building it so it survives

Make branch creation idempotent. Check for an existing branch with the same name before creating, since webhooks repeat.

Handle the resume delay with a connection timeout that allows for it.

Delete branches deliberately and report what was deleted, since a cleanup automation that removes the wrong branch is a bad day.

Keep the management API key separate from database credentials, and scope each.

Report the outcome. Branches created and deleted, their ages, and the consumption they accounted for.

Limits worth knowing about

Branch count and storage are plan-dependent, and unattended branch creation will find the limit.

Point-in-time retention is configurable and bounded, so recovery is possible within that window and not beyond it.

Cold starts are real and affect anything latency-sensitive that connects infrequently.

Pooled connections have session-state constraints exactly as any transaction pooler does.

Compute size is configurable and a branch created with a default size may be under-provisioned for a migration rehearsal on a large dataset.

What to build first

Branch cleanup: a scheduled sweep removing branches whose pull request has closed or which are older than a threshold, reporting what it removed. Build it before the branch-per-pull-request automation, not after, because the creation half is the fun part and the accumulation is what makes people turn the whole thing off.

Two habits make the difference. Report what was deleted with its age and its pull request, so the sweep is auditable. And exclude anything named as long-lived explicitly, since somebody will have a branch they need to keep and a cleanup that removes it will not be forgiven.

Frequently asked questions

How is branching different from a restore?

A branch shares storage with its parent and diverges on write, so it is created almost instantly regardless of database size and it is a fully writable Postgres database. A restore copies data and takes time proportional to how much there is.

Does a test branch contain real customer data?

Yes, unless you anonymise it. A branch carries whatever its parent held, so if that is production, the branch is production data in a testing environment. Anonymising on creation is a decision worth making before the first branch, not after.

Why does the first query take so long?

Compute suspended because nothing was connected, and it is resuming. It is the mechanism behind scale to zero. Allow for it in your connection timeout rather than treating it as a failure.

Should I use the pooled connection string?

For anything with concurrency, yes. Use the direct string for migrations and anything needing session-level features, since transaction pooling breaks session state including LISTEN and some prepared statement handling.

How do I stop branches accumulating?

Automate the deletion at the same time as the creation, and sweep weekly for orphans since a webhook will eventually be missed. Branch count and storage are plan-limited, and an unattended creation automation finds that limit.

Can I rehearse a migration safely?

Yes, and it is one of the better reasons to be here. Branch, run the migration, report duration and locking behaviour, delete the branch. The rehearsal is cheap and the information is exactly what you want before running it for real.

Is this actually Postgres?

Yes — the wire protocol, the extensions available on your plan, and ordinary Postgres clients all work. The differences are architectural: separated storage and compute, branching, and suspension when idle.

How should a branch be anonymised?

Replace values rather than masking them, keep the shape so the data stays useful, map consistently so joins still work, and handle free-text fields where personal information hides. Run it immediately after branch creation and fail the branch if it does not complete.

Is it worth branching production for an incident?

Yes. A branch from just before the problem gives you an investigation environment with the real data and no risk to production, created in moments. It is one of the few genuinely new capabilities the architecture provides.

How do I attribute cost across branches?

Read the consumption metrics per branch and project, and report compute hours and storage against whoever created each branch. Branch-based workflows make it unusually easy to accumulate spend that nobody recognises as theirs.

Get started today

Your first workflow is free to build.

Describe what you need. Cody handles the build, the connections, and the deployment.