How We Make Sure a Milo Update Never Breaks Your Store
Subscriptions are not a feature you can afford to get wrong. Behind every active subscription is a customer who expects to be charged the right amount, on the right day, and a store owner who expects that money to land.
By Rémi Corson · June 16, 2026 · 6 min read
A small bug in the wrong place is not a cosmetic glitch. It is a missed renewal, a double charge, or a customer locked out of content they paid for.
That is why, before any change to Milo Subscriptions reaches your site, it has to pass through a series of automated checks. Here is what that looks like, without the jargon.
We test the whole journey, not just the pieces
A lot of software testing checks tiny isolated parts in a vacuum. That is useful, but it is not enough for subscriptions, because the bugs that hurt the most happen between the steps: a switch that forgets to keep the next payment date, a migration that drops a sign-up fee, a renewal that ends up on hold for no reason.
So most of our tests are what we call integration tests. Instead of checking one function on its own, they spin up a real WordPress and WooCommerce, create a real subscription, and then run it through real situations: a customer upgrades their plan, a renewal succeeds (or fails), a store imports its subscriptions from another plugin, someone cancels. The test then checks that the outcome is exactly what it should be, down to the dates and the amounts.
In plain terms: we rehearse the things your customers and you actually do, automatically, every single time.
What one of these tests actually rehearses
Let us make it concrete. Say a customer on your Basic plan upgrades to Premium. That sounds simple, but a lot has to happen correctly in one move. The old plan has to be swapped for the new one. The price has to update. The next payment date has to be preserved, not reset, so the customer is not charged again tomorrow by accident. The change has to be recorded so you can see what happened.
One of our tests does exactly this, start to finish, and then checks every one of those details. If a future change accidentally resets that payment date, the test fails immediately and the update is stopped before it ever ships. The customer never feels it, because the mistake never leaves our hands.
We have tests like that for renewals, cancellations, failed payments, trials, sign-up fees, shipping address changes, and more. Each one is a small, patient rehearsal of something real.
A few real examples
This is not theoretical. Here are a few things our checks have quietly caught or now guard against.
An upgrade must touch only the right plan. When a customer moves from one plan to another, the change has to apply to that subscription and nothing else, even if they have other items in their cart at the time. A test rehearses exactly that, an upgrade made while an unrelated product sits in the cart, and confirms the upgrade only ever attaches to the customer’s own subscription. The day a future change blurs that line, the test fails and the release stops before it ships.
A first payment must match the real calendar. When renewals are synchronized to a fixed day, the first payment is prorated so customers pay only for the days they actually get. That math is easy to get subtly wrong, for example treating a plan billed every three months as if it billed monthly, which is a real difference in what someone pays. A test checks the discount against the true billing calendar on every release, so the amount and the schedule can never drift apart.
Every cent must survive, in every format. Much of the world writes prices like “12,50” with a comma. It is exactly the kind of detail that quietly breaks rounding. A test enters that value and confirms the full amount is saved, down to the last cent, so a parsing slip can never eat the decimals.
None of these are dramatic on their own. Added up over years, they are the difference between software you can trust with your revenue and software you cannot.
Moving from another plugin, safely
One of the most stressful things a store owner can do is move existing subscriptions from one plugin to another. Get it wrong and you can lose billing schedules, payment details, or the link between a subscription and its past orders.
So our importers, including the ones for WooCommerce Subscriptions, WP Swings and YITH (more to come!), are tested both ways: the import is rehearsed end to end, and so is the rollback. Your original data is copied, never moved, and an import can be reverted, putting everything back as it was. We do not just hope that works. We have tests that import a subscription, undo it, and confirm the original is restored exactly. You get to try before you commit.
With WooCommerce or without it
Milo Subscriptions runs both as a WooCommerce extension and in a standalone mode that does not need WooCommerce at all. That is two quite different worlds, and a fix in one must not break the other.
So we test both. The same lifecycle, renewals, emails, cancellations, gets its own rehearsals in standalone mode, separate from the WooCommerce ones. When we add something new, we are explicit about which world it belongs to, and the tests for the other world keep standing guard.
Nothing merges until every gate is green
Tests are only one of the checks. Before any change is accepted, it also has to pass:
- Coding standards, so the code stays consistent and readable, which makes future bugs easier to avoid.
- Static analysis, a tool that reads the code and flags suspicious patterns before they ever run, like using a value that might not exist.
- The full test suite, the hundreds of scenarios above.
All of this runs automatically. If even one check fails, the change does not go in. There is no “we will fix it later.”
We also use it like you do
Automated tests are fast and thorough, but they do not have eyes. So for anything visible, like a new screen, a button, or a block, we also open it in a real browser and use it the way you would: click through it, fill the forms, check it looks right and behaves right. Code can be correct and still feel wrong, and that final human check is where we catch it.
Why this lets us move faster, not slower
It would be easy to assume all this testing slows releases down. It does the opposite. Because the safety net is there, we can ship improvements often and with confidence, instead of sitting on changes for months out of fear. The recent 1.3.0 update, with editable standalone emails, a new importer, a Products view, and a long list of fixes, went out as one coherent release precisely because every part of it was verified before it left our hands.
Today Milo Subscriptions ships with more than two hundred of these automated tests, and the number grows with almost every update. Whenever we fix a bug or add a feature, we add a test that locks in the correct behavior. That test then stays forever, quietly guarding against the same mistake ever coming back. The work we did six months ago is re-verified on every release.
What this means for you
You should not have to think about any of this. That is the whole point. You run your store, your subscriptions renew, your customers stay happy, and the machinery that makes sure an update is safe stays quietly in the background, doing its job on every single release.
And if you ever do hit something that looks off, tell us. It usually becomes a new test the same week, so it can never happen to you, or anyone else, again.
Try the engine, it costs nothing
Free on WordPress.org, and one percent only when you get paid.