STAY UP TO DATE ON BIG DATA

How to Run a Production Database Migration with an AI Agent: Task Files, Review Gates and Claude Code on AWS

An AI agent ran our production database migration, and an engineer reviewed every task before the next one began. The agent is not what made that safe. The workflow around it did: a written plan, small task files, one review per task, and gates that stay in the engineer's hands.

The virtual machine that hosted our PostgreSQL on Scaleway had gone from about $3 a month to about $30. It was the last piece of our stack outside AWS, and the database behind both our sites, prod and poc. Between 23 and 30 September we moved both to an EC2 instance in eu-west-1. Claude Code wrote the tooling, ran the checks and both cutovers, and kept the records. We set the goals, made the decisions and approved the risky calls.

This post is less about PostgreSQL than about how the work was organised, because that is the part we will reuse. It is also an honest account: the workflow caught most problems before they mattered, and twice it did not. What we ended up with:

  • Task files — the plan broken into small markdown files, each with a status, a "Changes AWS" field, a checklist, a review focus and a log of what actually ran
  • A review loop — one task at a time; the agent stops at review and moves on only after we approve
  • A decision log — 16 places where the work turned out to differ from the plan, each with the evidence, the options and the decision once we made it
  • Permission gates — Claude Code's auto mode for routine work, and manual approval of each call for the risky steps
  • Stand-ins and memory — local fakes for AWS and PostgreSQL so the tooling is tested before it touches anything, and memory notes that carry lessons from one session to the next

The setup: a Django site on AWS Lambda through Zappa 0.57, with its database settings read from SSM Parameter Store, and a database moving from PostgreSQL 9.5.24 on Scaleway to PostgreSQL 12.22 in Docker on a t4g.micro instance. The agent was Claude Code, running in the editor inside the project's devcontainer.

The workflow as a loop. A plan file is broken into a tracker and task files. Each task runs as one loop: the engineer says start task N; Claude Code reads the task file, the tracker and its memory; Claude writes the tooling and tests it against local stand-ins; a gate asks whether the task changes AWS, and if so the engineer gives a go-ahead and approves each call; Claude runs the steps, records the Log and sets the status to review; the engineer reviews the Log, decides and marks the task done, and the loop starts again with the next task. Underneath, the files that survive between sessions: the task Logs, the tracker, Claude's memory notes and the deploy/db scripts. One task, one loop. Claude Code does the work between the engineer's two messages, a step that changes AWS waits at the gate in the middle, and everything worth keeping ends up in files, not in the chat.
  • 28Claude Code sessions, 23–30 September
  • 16places the work differed from the plan, each logged with its evidence
  • 7 min 38 spublic downtime for the prod cutover
  • 38 msmedian Django time on prod, down from 397 ms

How It Started: A Tenfold Bill and a Written Plan

Everything else already ran in AWS: Lambda and API Gateway through Zappa, CloudFront, S3, SES, ACM and SSM Parameter Store. Only the database lived on Scaleway, and Lambda reached it over the public internet. A tenfold price rise with nothing added made it the obvious piece to move.

Before any work started there was a plan, written on 7 September and kept in the repo at plans/postgres-scaleway-to-aws-migration.md. It recorded what the codebase told us, and one fact shaped everything after it: Django's DATABASES is not in the repo. It is executed from an SSM parameter when the settings load, one parameter per stage. A cutover is therefore an edit to one parameter and a redeploy, and poc and prod can move independently.

The plan chose the simplest path it could defend: a public EC2 instance running PostgreSQL 12 in Docker, poc first, then validation, a seven-day soak and only then prod. It priced a t4g.micro at about $14.56 a month on demand, against $30. It ended with a table of gates, one per phase, saying what had to pass before the next phase could start.

It was a good plan, and it was wrong in places nobody could see from a desk. The biggest: it assumed Scaleway ran PostgreSQL 12, probably because local development did. It ran 9.5.24. The workflow below is what found that, and fifteen other differences, before they could hurt.

What Is an Agent-Run Migration?

An agent-run migration is one where the agent does the work, the repo holds the state, and the engineer holds the gates. No single conversation holds the migration: each Claude Code session starts cold, reads the files, does one task and writes down what happened.

Component Role in the workflow
Plan The why, the target design, the phases and the gates, written before any task
Tracker README.md next to the tasks: a "Now" paragraph, the task tables, the rules and the decision log
Task file One reviewable unit: why, steps, a "Done when" checklist, a review focus and a Log
Session One Claude Code conversation, normally one task; it knows only what the files and memory tell it
Permission mode Auto mode, where a classifier allows or refuses each call, or manual mode, where the engineer approves each one
Memory Notes Claude writes for its later sessions: the workflow rules, where the test stand-ins live, lessons learned
deploy/db/ What the tasks produce: scripts, configuration, a runbook and a README for whoever runs the database next

Step 1: Breaking the Plan into Reviewable Task Files

The first message, on 23 September, was about process, not PostgreSQL:

Given the migration plan break down the work into small easy to review tasks written as md files that you track and update. Wait for my review before moving to the next task. Start with POC first.

An earlier version, sent four minutes before, asked for a git branch per task, pushed for us to merge. We stopped that session and restarted with a simpler gate: wait for my review. In practice that suited the work. Many tasks changed little in the repo and a lot in AWS, and what needed reviewing was the Log of what happened rather than a diff.

Claude broke the poc path into task files, from backing up the settings to the soak sign-off, and wrote a tracker. The list kept changing as the work taught us things: when backups became on-demand, the backup-alarm drill had nothing left to test, so it was dropped. The prod and decommissioning tasks, P1–P4 and D1–D3, were only written during the soak, once poc had shown what prod would need.

plans/ ├── postgres-scaleway-to-aws-migration.md # the plan └── postgres-migration-tasks/ ├── README.md # tracker: Now, rules, tables, decision log ├── 01-snapshot-ssm-settings.md ├── ... # 02–21: the poc path ├── 21-soak-signoff.md ├── P1-prod-baseline.md # P1–P4: prod ├── ... └── D3-close-out.md # D1–D3: decommission

The tracker opens with the rules both sides work to, quoted here lightly trimmed:

  • One task at a time. When I finish a task I set it to review and stop. I move on only after you approve it.
  • Each task file has: what it delivers, the steps, a Done when checklist, a Review focus list and a Log that records what actually happened.
  • Nothing that changes AWS runs without your go-ahead in that task. Tasks that only write repo files never touch AWS.
  • Secrets never go into the repo or into these files.

Every task file starts with the same header. Here is the poc cutover's, trimmed to its outline:

# 17 · Run the poc cutover

| | |
|---|---|
| Status | done |
| Plan | Phase 2.1–2.4 |
| Who | Claude, **after your go-ahead**, with you available during the window |
| Changes AWS | **yes**: poc API Gateway stage, the SSM staging parameter, a poc deploy |
| Depends on | 16 |

## Done when
## Log

Two fields carry most of the weight. Changes AWS tells the reviewer, before reading anything else, whether this task can hurt anyone. Log is where the evidence goes: UTC times, command IDs, row counts and anything that differed from the steps. A task moves from not started through in progress and review to done. waiting on you means there is something only the engineer can do, and blocked and dropped cover the rest.

Timeline of the migration by day. Wednesday 23 September: tasks 01 to 07, prerequisites and tooling. Thursday 24: tasks 08 to 11, backups and VM scripts; 12, provisioning the VM, which changed AWS; 13, the phase 1 gate; 14, the migrate scripts; 15, the trial restore, which changed AWS. Friday 25: 16, cutover preparation; 17, the poc cutover, live on AWS at 10:30 UTC; 18 and 19, validating poc; 20, the restore drill; 21, the soak begins. Saturday and Sunday: soak, no sessions. Monday 28: 21, soak signed off after 68.6 hours; P1, prod baseline; P2, preparation and a rehearsal deploy; P3, the prod cutover, live on AWS at 10:13 UTC; P4 begins. Wednesday 30: P4, prod validated. Next: D1, a 14-day hold to 12 October; D2, destroying the Scaleway VM; D3, close-out and the PostgreSQL 16 deadline of 31 March 2027. Eight days from the first message to a validated prod. Coral marks the tasks that changed AWS: a handful, among many more that only read AWS or wrote files.

Step 2: One Task per Session, Then Stop

From there the rhythm hardly changed. A session began with "start task 14 of the migration" and ended, anywhere from twenty minutes to two hours later, with "mark this as done". In between Claude worked through the steps, tested what it wrote, recorded the result in the Log, set the status to review and stopped. Over 28 sessions we sent 98 messages, most of them a few words long.

The messages in between are where the engineering happened. Task 12, provisioning the instance, is a good example:

start task 12 of the migration
do not use arm64. i want a x86 image.
will i be able to run the arm64 on my linux machine easily?
continue with arm64
i switched to manual, please continue with provision
format it by hand
done
mark it as done

That is a change of mind after a question (an x86 machine pulls the native image, and dumps restore on either architecture), a switch of permission mode before the first real AWS change, a decision on something nobody expected (the first boot stopped at a guard because the new data volume did not read as zeros) and a step only a human could take (done meant we had clicked the confirmation link in the alarm subscription email). Every one of those turns is in the task's Log with its time.

We reviewed in the editor, not in the chat. The task file, or the script it produced, was usually open when the approval came: provision.sh for task 10, s3-lifecycle.json for 11, build-settings.py for 16. The task's Review focus said where to look, and the Log said what had happened. The chat only carried the decisions.

A one-word go-ahead is ambiguous

Task 16 prepared the poc cutover and stopped for review. We replied "execute". Claude read that as approval of 16 and a go-ahead for 17, and ran the whole poc cutover, with us approving each command. When we asked "did you do 16 or 17 here?" it said so plainly, and added that reading "execute" as "run 17" was a judgement call and that it should have checked before the freeze. Nothing went wrong, but nobody had decided to cut over that morning. Since then, a go-ahead names the task.

Step 3: The Engineer Decides, the Agent Brings the Evidence

The tracker has a section called "Where these tasks differ from the plan". By the end it held 16 items. Each one says which task found it, what the evidence was, what the options were and what Claude recommended, and, once we had decided, what and when. A few are still open. It is the most useful part of the tracker, because it is the record of every judgement call.

The first big one came from the engineer's terminal, not the agent's. Auto mode would not let Claude read the Scaleway password and connect, so Claude wrote a script and we ran it. It failed with syntax error at or near "PASSING", and we pasted the error back. Claude knew that XMLTABLE has been part of the grammar since PostgreSQL 10, so only an older server gives that error. The rerun confirmed it: 9.5.24, on x86_64. The plan's "same major version, no upgrade" premise was gone, and the choice of target was ours to make again.

What the work found Task What we decided
Scaleway runs PostgreSQL 9.5.24 on x86_64, not 12, and holds four databases besides ours 03 Stay on PostgreSQL 12 on EC2 as planned, move only the website's two databases, use a t4g.micro
The CA's private key had no home in the plan 06 Keep keys and certificates in SSM and reuse them while they are valid
PostgreSQL 12's JIT turned the page-menu query from 0.1 ms into about 180 ms, and prod had reached 106 concurrent Lambdas 05 jit = off and max_connections = 120
The plan had nightly dumps, daily snapshots and a freshness alarm 08, 11 Backups on demand only, deleted after 30 days, no snapshots, no alarm
The plan's freeze (reserved concurrency of 0) is removed by zappa update 16 Freeze by throttling the API Gateway stage to 0 instead, rehearsed on poc first
The soak was planned for 7 days 21 End it after 68.6 hours: "metrics are enough so far, let's conclude the checks."
The plan's window was a Sunday night P2 The same Monday, in daytime traffic, with a target of under 10 minutes

Two patterns stand out. First, we did not always take the recommendation, and the workflow did not need us to: Claude proposed the quietest hour of the week, Sunday at 02:00 UTC, for the prod window, we cut over the same Monday in daytime traffic, and the task recorded both. Second, the agent's job was to make each trade-off explicit before we chose it, not after.

Know what you chose

On-demand backups were our decision, and the tracker spells out what it costs. The recovery point is the time since the last dump someone ran, not "up to 24 hours", and after 30 days without a dump there is no backup at all. A restore drill of each stage's first dump took 3.0 s on poc and 2.9 s on prod, so restoring is fast. Having something recent to restore is now a habit we have to keep.

Step 4: Permissions Are a Gate, Not a Reviewer

Three things stand between a command Claude writes and AWS. The task's rule says nothing that changes AWS runs without a go-ahead in that task. Claude Code's permission mode decides, call by call, what runs without asking. And the scripts check their own preconditions. A fourth, the fail-closed test harness, came later, for reasons in the next step.

Four gates between a command Claude writes and AWS. Gate 1, the task rule, held by the engineer: nothing that changes AWS runs without a go-ahead in that task. Gate 2, the permission mode: auto mode's classifier allows or refuses each call, or in manual mode the engineer approves each one. Gate 3, script guards in deploy/db: for example the cutover's window steps refuse to run unless the stage is frozen, and every step prints RECORD lines. Gate 4, added after the P2 incident: a fail-closed test harness with bogus credentials, a dead endpoint and a check that every stand-in is the one that runs. Below, the P2 incident: a stub test of the cutover script ran the real aws command; gate 1 did not apply because it was meant to be a test, auto mode did not stop it, and the chain stopped at the settings step only because the same bug pointed it at the wrong home directory. Defence in depth, with an honest bottom row. When the harness bug sent real commands to prod, the first two gates did not apply and the third stopped the chain by accident. The fourth exists because of that.

Auto mode is the convenient one, and it carried most of the read-only work: Logs Insights queries, metrics, read-only Run Commands on the instance. It was also hard to predict. The same kind of call got different verdicts on different days:

Call Task Auto mode
Write the decrypted settings to a backup file 01 ✗ refused
Run provision.sh against the real account 12 ✗ refused ("Production Deploy")
Run provision.sh, deploy poc, upload to S3 15 ✓ allowed
Download the prod Lambda package to read it 15 ✗ refused ("Production Reads")
The same download P1 ✓ allowed
Record our go-ahead in a task file 15 ✗ refused ("Instruction Poisoning")
A read-only preflight on prod P2 ✗ refused ("Production Reads")
A stub test that froze the prod stage and restored prod's data P2 ✓ not stopped

So for the steps that mattered most we switched to manual mode and approved each call: provisioning, the secret-store writes, both cutovers and prod's validation. Refusals were not dead ends either. When auto mode would not let Claude handle a credential, Claude wrote and tested the command, handed it to us to run, as in tasks 01 and 03, and carried on from its output.

Manual mode has a cost, and the poc cutover measured it. The site answered 429 for 13 min 51 s. The commands took about 3 minutes of that and the deploy nearly 4; the rest was the time between approvals. For prod, task P2 turned the runbook into deploy/db/cutover-prod.sh, a file with one shell function per step, each of which checks its own output and prints RECORD lines with UTC times for the Log. The prod window became four approved calls instead of one per step.

Auto mode is not a reviewer

It refused a read-only preflight on prod and allowed a test harness to freeze prod. A classifier judges one call at a time, without the task's context. Treat it as a convenience for routine work, and keep the decisions that matter in the task rule and in manual approval.

Step 5: Testing Against Stand-ins, and Making Them Fail Closed

The devcontainer had no psql, no PostgreSQL server and no Docker. Rather than test against AWS, Claude built stand-ins in its scratch space and recorded where they lived in memory:

  • the pinned image's PostgreSQL 12.22 binaries, unpacked and run directly
  • a fake SSM API for the real AWS CLI, which could also simulate denials and races
  • moto 5.2.3 for EC2, IAM, Route 53, S3 and CloudWatch, plus a small Data Lifecycle Manager stand-in, because moto has none
  • PostgreSQL 9.5.24 as a fake Scaleway, with TLS, md5 passwords and a password containing spaces, quotes, a backslash, $ and :

That let the tests be thorough in ways a single real run never is. provision.sh was run first, again, after stopping halfway, and as a rebuild. An extra inbound rule for port 22, a policy edited by hand or a DNS record with another address each had to stop it or be put back. migrate-db.sh was killed with SIGTERM, SIGINT and SIGHUP mid-dump, and checked to leave no dump and no password file behind. The stand-ins also have limits, and the README says which: moto confirms email subscriptions at once and has no default bucket settings, and no fake predicted that a brand-new encrypted volume would stop the format guard in task 12.

Then, during P2, a stand-in failed open. Claude wrote a local harness to test cutover-prod.sh with fake aws, curl and zappa first on the PATH, and set it up with this line:

export STUB=$(dirname "$(readlink -f "$0")") HOME=$STUB/home PATH=$STUB/bin:$PATH

Bash expands every word of that line before it assigns any of them, so $STUB was still empty when PATH was built, and the real tools ran. The harness chained every step with &&. It froze the prod stage, took a real EBS snapshot and ran prod's trial restore, which we had not yet approved. It stopped at the settings step only because the same bug had pointed HOME at the wrong place, so the new settings file could not be found. Claude killed the run, put the throttle back, checked that the settings, the Lambda and the concurrency were untouched, wrote the incident into P2's Log and the tracker, and opened its report with it:

I caused a short prod outage while working on P2. The prod site returned `429` (too many requests) to everyone for at most 3 min 10 s, from 07:30:23 to 07:33:33 UTC. It's back, and nothing is left broken.

API Gateway counted 26 4XX in those minutes, and 22 were the harness's own requests. The report went on with the cause, the impact, what had been checked, and two decisions for us: keep the accidental restore or rerun it properly, and keep or delete the snapshot. We chose a rerun. The replacement harness fails closed. Here is its guard, the first half of the file; the rest resets the stand-ins' state, sources the script and runs the case:

# run.sh '<commands>': sources cutover-prod.sh from the stand-in repo, with the stand-ins first on PATH.
# Fails closed: bogus AWS credentials and endpoint, a dead proxy, and it aborts unless every tool
# the file calls resolves to its stand-in. BREAK=1 leaves PATH alone, to show that the guard fires.
STUB=$(dirname "$(readlink -f "$0")")
export STUB
export HOME=$STUB/home
[ -n "$BREAK" ] || export PATH=$STUB/bin:$PATH
export AWS_SHARED_CREDENTIALS_FILE=/dev/null AWS_CONFIG_FILE=/dev/null
export AWS_ACCESS_KEY_ID=stub AWS_SECRET_ACCESS_KEY=stub AWS_ENDPOINT_URL=http://127.0.0.1:9
unset AWS_PROFILE AWS_SESSION_TOKEN AWS_DEFAULT_PROFILE
export https_proxy=http://127.0.0.1:9 http_proxy=http://127.0.0.1:9 HTTPS_PROXY=http://127.0.0.1:9 HTTP_PROXY=http://127.0.0.1:9 ALL_PROXY=http://127.0.0.1:9
for t in aws curl zappa sleep; do
  [ "$(command -v "$t")" = "$STUB/bin/$t" ] || { echo "ABORT: $t resolves to '$(command -v "$t")', not the stand-in"; exit 99; }
done

It ran 20 cases without reaching AWS, and it found a real hazard on the way: the settings and deploy steps would have run on a live, unfrozen stage. They now refuse unless the stage is frozen. The lesson went into memory as a rule for every later session, and it is the one we would put first for any team doing this:

Stand-ins must fail closed

A local test of a script built to act on real resources must not rely on PATH alone. Give it bogus credentials and an endpoint that cannot be reached, check with command -v that every stand-in is the one that runs before anything is sourced, run one read-only step before any chain, and never run a mutating chain in the background.

Step 6: Memory and Logs, or What Survives Between Sessions

None of the 28 sessions remembered the one before it. What carried the migration was files: the tracker's "Now" paragraph, which says where things stand in a few sentences, the task Logs, and four memory notes Claude wrote for itself. One holds the workflow rules and every permission lesson, one says where each test stand-in lives, one is the fail-closed rule, and one is about money.

That last one started with an email. AWS told us the account had used up its CloudWatch free tier for September, and we asked whether the migration had caused it. It had. Logs Insights bills by the bytes scanned over the whole time range, whatever the query filters, and a 30-day query on the prod Lambda log scanned about 1.15 GB. Two tasks had run five of them: 6.07 GB, against 5 GB free a month. The memory note says to query poc first, keep prod windows short, get a query right on a narrow window before widening it, and check the cost afterwards. Later prod queries were sized to the window they needed: the cutover's error query scanned 34 KB.

An agent in sessions does not keep time

The soak needed one check a day, each less than 24 hours after the last, because poc keeps its Lambda log for one day. Claude works only when it is prompted, and nothing prompted it on days 1 and 2, so those logs were gone by the time anyone looked. The metrics and the PostgreSQL log covered the gap, and we accepted that. Any check that depends on the clock needs an owner who is not the agent.

Prod Cutover Flow: One Window Through the Workflow

By the time prod came round, the workflow had done its job: almost nothing in the window was new. P1 took prod's baseline. P2 built the new settings file and had us review a redacted diff, ran the trial restore on prod's data, and rehearsed the prod deploy with the settings unchanged, so the cutover deploy would carry nothing else. P3 was the window, run from the prepared shell in four approved calls:

  1. Freeze Throttle the prod stage to 0, wait for ten 429s in a row, snapshot the data volume, then wait 60 s for requests in flight.
  2. Copy and verify migrate-db.sh prod --replace dumps Scaleway, restores into PostgreSQL 12 and compares both sides: 44 tables, 1,634 rows and 36 sequences, all 344 lines matching, in 19.7 s.
  3. Point the settings Write the reviewed settings file to SSM, as version 3.
  4. Deploy deploy_zappa.sh prod, then check that the stage is still frozen.
  5. Check migrate --plan --check shows nothing pending, and a smoke test inside the Lambda reads, writes and connects with verify-full over TLS 1.3.
  6. Unfreeze Put the throttle back and wait for the first 200. From here on we fix forward.
  7. Back up Dump the migrated database to S3. P4 restored this dump in its drill.
Two charts on a minutes scale. Top: public downtime of the poc cutover, 13 minutes 51 seconds, against the prod cutover, 7 minutes 38 seconds, with a dashed line at the 10-minute target. Bottom: the prod window from 10:05:37 UTC. Freeze until ten 429s in a row at 10:06:10, the snapshot at 10:06:11, the 60-second wait to 10:07:11, copy, verify and settings to 10:07:37, then approval and the deploy, which ended at 10:10:49, a gap of about two minutes, the checks at 10:12:47 to 10:12:48, the unfreeze at 10:12:58 and the first 200 at 10:13:15. The database copy is the short part. Most of the window is the deploy and the gaps between approved calls, which is why preparing the shell nearly halved the downtime.

Prod has served from the new instance since 10:13:15 UTC on 28 September. The site answered 429 for 7 min 38 s, against 13 min 51 s for poc, and the window had 0 database errors, 0 Lambda errors and 0 5XX. P4 validated it over the next 45 hours: the median Django time for successful requests fell from 397 ms on Scaleway to 38 ms, the improvement the plan expected from a database in the Lambda's own region, and the restore drill of prod's first dump took 2.9 s.

The migration is not closed yet, and the task files say what is left. D1 holds Scaleway untouched for 14 days, to 12 October, then takes a final archive. D2 destroys the Scaleway VM, which is what stops the $30 a month. D3 closes out, and carries the plan's dated exit: PostgreSQL 12 gets no more security patches, so the upgrade to 16 is due by 31 March 2027, with the same dump-and-verify tooling.

What Changes for Engineers

Looking back over the sessions, the split of work was clear, and it is the split we expect to keep:

The engineer Claude Code
Set the goal, the order (poc first) and the review rule Broke the plan into task files and kept the tracker current
Made the call on each difference from the plan Found them, with evidence, options and a recommendation
Switched to manual mode and approved each call in the risky steps Wrote and tested deploy/db/ against stand-ins, then ran it
Ran the commands auto mode would not let Claude run Wrote those commands so they printed no secrets
Did the browser checks, clicked the email links, chose the window Recorded every step with its UTC time and result
Asked questions before deciding Wrote the lessons into memory so later sessions kept them

The engineer typed few commands and made many decisions. That is the shift: the leverage moved from typing to judgement, and the work that is easiest to skip — checking, recording, writing the runbook — was done on every task, because each task asked for it. Our recommendation for a team running its next change this way:

  1. Write the plan first The why, the target, the phases and a gate per phase. The tasks test it; expect them to correct it.
  2. Ask for task files, not a big run Small enough to review in one sitting, with "Changes AWS" in the header and a Log for what actually ran.
  3. One task per session, then stop The agent sets review and waits. Review the Log and the files, not the chat.
  4. Decide in writing Every difference from the plan goes into the tracker with its options and your decision, including when you overrule the recommendation.
  5. Name the go-ahead "Go ahead with 17", not "execute". Switch to manual permissions before anything that changes production.
  6. Make stand-ins fail closed Bogus credentials, a dead endpoint, a command -v check, no chained mutating steps.
  7. Give the clock an owner Soaks and daily checks need someone, or something, that starts a session on time.
  8. Ask before you commit Before committing the migration's files, our question to Claude was "is there any sensitive information?"

Summary

An AI agent can now carry the whole of an infrastructure change like this one: writing the tooling, testing it, running the cutover and documenting every step. That does not make the engineer optional. It makes the engineer's part smaller in time and larger in consequence: the decisions, the go-aheads and the questions. The workflow is what keeps those decisions with the engineer.

We moved prod and poc off Scaleway in eight days, with 7 min 38 s of prod downtime and a tenfold drop in median Django time. The work ran as 21 poc tasks plus seven for prod and decommissioning, each reviewed before the next began, with 16 tracker items and four memory notes. It also produced two process failures, a misread go-ahead and a test harness that failed open, and both turned into rules.

The next run already has a date: the PostgreSQL 16 upgrade, due by 31 March 2027. It can start the same way, with a plan, and then one sentence asking for small, easy-to-review tasks.

Author Image

Sebastian Brestin

Sebastian founded Qwertee in 2017. He holds a BS in computer science from Babes Bolyai university (Cluj-Napoca, Romania). His expertise ranges from backend development to data engineering, and he has a keen interest in network and security related topics. His experience includes working in multinational corporations such as HP but also in a fast paced startup environment. Sebastian has a wide variety of interests such as learning about video game design or meeting up with the local startup community.