Skip to content

Build1 publisher3 min readPublished

Repointing nameservers closes the window where old and new DNS can be compared

A dev.to walkthrough for e-commerce zone moves puts enumeration, a saved raw snapshot and a diff ahead of the first write. The Python runner it ships retries HTTP 429 five times and treats every other error as fatal.

The Engineer · Build desk

Illustration accompanying Repointing nameservers closes the window where old and new DNS can be compared

What happened

  • A dev.to walkthrough for e-commerce DNS migrations orders the work as list the live records, save the snapshot, diff against the intended set, upsert only the differences, then verify before repointing nameservers.
  • The post treats the existing registrar as evidence: enumerate every record and store the raw response before normalizing anything, because that stored file is the rollback material.
  • Export, import and repoint immediately is fast in a staging notebook, and once delegation changes a missing record is an incident instead of a line in a diff.
  • The sample Python runner retries only HTTP 429, giving it five attempts with Retry-After or exponential backoff, and raises on any other unsuccessful status the first time it sees one.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint Verification has to finish while the old nameservers still answer. The length of that overlap sets how fast a zone can move; the write throughput of the DNS API does not.
  • decision Deletion stays a human answer on the first pass, so a customer zone cannot be onboarded end to end without someone ruling on the records that exist only upstream.
  • capability Because the intended set is applied by upsert, a run that dies halfway can be pointed at the same files again and re-run until the diff comes back empty.
  • exposure The exposure sits with the merchant: if the old zone was only ever copied into a spreadsheet by hand, the mail authentication records have no verified original to restore from.

Only HTTP 429 gets a second chance in the sample runner. Every other unsuccessful status raises RuntimeError on the first response, so a 503 from the registrar ends the run wherever it happens to be [23]. Rate limiting gets five attempts, honoring Retry-After when the header is present and sleeping 2**attempt plus a random fraction of a second when it is not [16].

Add the fallback delays: 1, 2, 4, 8 and 16 seconds, which is 31 seconds of sleep before the error reaches the caller [1]. The last sleep lands after the fifth attempt, which the loop has already decided not to repeat. Each request also carries a 30 second timeout [18], so one badly behaved call can hold the runner for 150 seconds of requests on top of the backoff [2].

Idempotency is opt-in at the transport layer. call() attaches an Idempotency-Key header only when the caller passes one [17]. The snippet imports hashlib and defines stable_json, which serializes with sorted keys and compact separators [19]. Sorted-key serialization is what makes a content hash stable across runs. The excerpt stops inside main()'s argument parsing, at the default for --snapshot [22].

The diff runs on normalized records, and the post normalizes only the properties that define intended record identity and value, then reviews additions, changes and source-only entries [7]. The identity choice decides what the reviewer sees: put a mutable property in the identity and an edited record arrives as an addition plus a source-only entry; leave it out and the same edit arrives as one change.

Ownership changes who signs off, and the post keeps the machinery identical. Customer-owned zones get an import-and-approval boundary, platform-owned zones can start from a versioned template, and the template path is still told to enumerate first [4][11]. For a customer zone the approval artifact holds the saved source, the intended set, the computed changes and the verification result [10].

The legacy storefront is Node.js, and the post argues the migration runner does not have to inherit its registrar SDK [13]. "I prefer a small Python control plane because the input and output files drop directly into an eval harness," the author wrote [14]. The request payloads are JSON files built against the live discovery schema, so the runner does not guess vendor fields outside the contract [15]. Domain verification and account-side evidence sit in the surrounding onboarding workflow, on the same key and base URL, and the completed verification result feeds the final receipt instead of a timed poll against a registrar API [21].

The post argues this ordering from how the tool works, and it does not report how often a forgotten record causes an incident, or on how many zones the runner has been used [24].

What to watch

  • Whether the full script derives the Idempotency-Key from stable_json, which is what would make a per-record retry safe after a mid-run abort.
  • Whether the onboarding workflow's verification result gates the nameserver change or only files the receipt.
  • Whether the runner ever gains a delete path for source-only records, and what approval that path requires.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories