Background
Nobody dares to try a new theme on a shop that is selling right now. Break one thing and customers see it at once, and orders can be lost. So Tan Doanh has two sites: tandoanh.vn is the real one, and td.phatdev.net is the test copy, where I build and try every change before touching the real shop.
A test copy is only useful if it looks like the real one. A different price, a different stock count, or a missing order makes every check on it doubtful. So my job is to keep the test copy in step with the real site, in one direction only: the real site is read only, and the test copy never writes back.

The problem
The reconciliation system watches 5,139 products and 213 variations (measured 8 Nov 2026). Prices and stock change all day long: staff save a product, a new order arrives, an item sells out.
Three things make this hard. The first is speed: if the test copy needs an hour to learn a new price, it is useless. The second is accuracy: one wrong product shakes trust in the whole copy. The third is safety: the test copy must not send real emails to customers, must not feed numbers into the real shop's analytics, and must not show up on Google.
How it was done
1. Three layers, each covering the gaps of the one before
I did not trust a single mechanism, so I built three layers.
The push on save layer is the main one. When staff save a product, a price, stock or an order on the real site, the real site sends a small notice to the test copy, and the test copy pulls exactly that item. According to my operations notes this takes about 10 seconds (I have not re-timed every run).
The nightly check at 03:10 compares the price and stock of every item again and reloads the ones that differ.
The manual run is one command that also pulls the slower-moving things: images, customers, old orders, reviews, the home page, logs.

2. Why the nightly check is needed
I found out that the push layer was not enough by looking at how the real site changes prices. A bulk price change through an import tool does not make WordPress record a new edit time for the product, so mechanisms that rely on edit time miss it. The nightly check covers exactly that gap. It builds a short fingerprint from each product's price, regular price, sale price, stock, stock status and product code, on both sides, and reloads only the items whose two fingerprints differ.
3. Match by ID, not by product code
This was the most expensive mistake I met. At first the test copy looked up the matching product by product code (SKU). But the real site has 135 duplicated codes (271 products), and 1,297 products with no code at all (figures from 2 Aug 2026). When two different products share one code, the test copy grabbed the first one and overwrote it. The log still said success. It happened twice with the same pair of hard drives.
The fix was to bring product IDs on the test copy into line with the IDs on the real site, then match by ID. Products created only on the test copy use an ID range above two million, so they can never collide with the real site.

4. The test copy must be harmless
The test copy has its own safeguards. Outgoing mail is blocked, including mail that goes through an outside mail service. The analytics tracking code is switched off so the real shop's numbers stay clean. The pages carry a tag asking Google not to index them, and crawler bots are refused at the web server. For customers, the test copy mirrors the whole list from the real site, and stops by itself if it finds an account that exists only on the test copy, so nothing is deleted by mistake.
Results
The numbers here come from the system's own reconciliation log, not from a separate test.
Before: a comment in the check's code records that until 2 Aug 2026 matching by product code skipped 1,297 products that have no code. After: the 8 Nov 2026 check log shows 5,139 products matched against 5,139 and 0 mismatches; matching by ID means products without a code are compared too. The two numbers come from two dates and two matching methods, so read them as an order of magnitude.
On the nights of 7 and 8 Nov 2026 the nightly check ran at 03:10 and compared 5,139 products and 213 variations. Both nights it found 0 mismatched items.
On 3 Nov 2026 I wiped the test copy's data and rebuilt it from the real site. That covered products, orders, customers, categories, and regenerating thumbnails for 34,360 images at the sizes the new theme uses. I took a backup first, and kept the test copy's own parts such as pages, menus and theme settings.
One thing I have not managed. According to my review of 3 Nov 2026, the push layer sends its notice only once, does not retry and does not keep a log. If a notice is lost, the item is only repaired when the nightly check runs, and that check only compares price and stock. A changed description, a replaced image or an edited category waits for the manual run.
Lessons
Even a good sync mechanism has gaps. What is worth doing is knowing where the gap is and placing one more layer right there.
The key used to pair two records must be something that is never duplicated. A product code sounds like a natural key, but real data differs from the theory.
A log saying success does not mean it is right. The overwrite bug above lasted because everyone trusted the word "ok". Since then I check using data from both sides, not the system's own report.
And a test copy has to be built to be harmless: one forgotten mail block is one customer receiving a test email.
Need something similar?
If you have a shop that is running and you need a safe place to test, or the data between two systems has drifted and you do not know where from, send me a few lines about the setup. I will look at how the two sides talk to each other first, then tell you plainly what is worth fixing.




