Janarayson

Moving MoneyMama's calculators to Astro without changing a single answer

Engineering7 min read

The three calculators on moneymama.co are the oldest code we have. This is what's inside them, and how we moved them from Gatsby to Astro with the numbers intact.

We moved moneymama.co from Gatsby to Astro last week. Most of the site is now plain Astro, but the three calculators are still React, and they’re the oldest code we have.

They were also the part that couldn’t be allowed to break. A marketing page that renders slightly differently after a migration is a cosmetic problem. A retirement calculator that gives someone a different number is not.

So this is the story of how the calculators came to be, what’s going on inside them, and how we moved them without changing a single answer.

Two days in March

The retirement calculator was the first thing I built for MoneyMama, in March 2025. I was still at Intrack at the time and helping out at FinLit on the side, and the site was on Gatsby.

It took about two days. You tell it your age, when you want to retire, what you earn, what’s in your EPF, what you’ve saved and how much you’d want to spend each month once you stop working. It tells you the number you’ll need by then, and how much to put away every month to get there.

The maths wasn’t mine. FinLit already had a Google Sheet that worked all of this out before I joined, so my job was to get the code to produce the same numbers as the sheet, cell for cell.

That’s why the code still carries its own copies of Excel’s FV, PV and PMT functions, complete with Excel’s sign conventions. It was the only way to be sure we matched.

A few assumptions sit underneath that the user never sees: inflation at 3%, salary growing 2.5% a year, EPF contributions at 11% from the employee and 13% from the employer (12% once you earn RM5,000 a month), and an EPF dividend of 5.5%.

The part of the maths I like most

Your EPF contribution grows with your salary every year, so the obvious way to project it is to loop through every working year and add the contributions up.

The sheet doesn’t do that.

It discounts the contributions at an adjusted rate, (dividend - growth) / (1 + growth), which turns a payment that grows every year into a flat one. That lets the whole stream be valued in a single present value calculation, which is then grown forward to retirement at the dividend rate and added to whatever is already in the account.

// Employer EPF rate varies by salary: 13% below RM5000, else 12%.
const EPF_EMPLOYER_RATE = monthlyIncome < 5000 ? 0.13 : 0.12;

const epfEmployeeYearlyContribution = monthlyIncome * EPF_EMPLOYEE_RATE * 12;
const epfEmployerYearlyContribution = monthlyIncome * EPF_EMPLOYER_RATE * 12;
const epfTotalYearlyContribution =
  epfEmployeeYearlyContribution + epfEmployerYearlyContribution;

const adjustedReturnForEpf =
  (EPF_DIVIDEND_RATE - INCOME_ANNUAL_INCREMENT) / (1 + INCOME_ANNUAL_INCREMENT);

const pvEpfContributions = PV(
  adjustedReturnForEpf,
  yearsUntilRetirement,
  epfTotalYearlyContribution,
  0,
  1,
);
const projectedEpfValue =
  -FV(EPF_DIVIDEND_RATE, yearsUntilRetirement, 0, -pvEpfContributions) +
  FV(EPF_DIVIDEND_RATE, yearsUntilRetirement, 0, epfBalance) * -1;

It’s a standard growing-annuity trick, and it meant the whole projection was a handful of lines instead of a loop.

The overachievers

In May 2025 we ran into a case I hadn’t designed for.

The result screen assumed everyone had a gap to close. But if your projected EPF and savings already beat your goal, the gap goes negative, and so does the amount you’re told to save every month.

We started calling these people the overachievers, and they got a screen of their own. Confetti, “you’re ahead of the game”, and the negative number turned into a monthly surplus: the extra you could spend each month and still get there. The emailed report changed too. It dropped the savings milestones and the fund suggestions, since there was nothing to close.

It’s not a rare case. A 30-year-old earning RM5,000 a month who wants RM3,000 a month at 60 gets there on EPF alone, with about RM174 a month to spare.

The old Gatsby result page: your results are ready, a goal of RM1,382,663, and you need to save RM481.68 a month, with a growth rate sliderThe old Gatsby overachiever page: great news, you're on track to achieve your retirement goals, a goal of RM1,235,388 against a projected RM1,411,489, and a yellow box about extra financial capacity
The result pages on the old Gatsby site, with a gap to close and for an overachiever.

Two more calculators

First Million came in November 2025, to go with the videos we were putting out at the time. You put in what you have, what you can save each month and an expected return, and it shows how many years until your first million, with milestones along the way.

Debt-Free is new with this move. You put in your credit card balance, the interest rate and what you pay each month, and it tells you how many months until you’re clear and how much interest you’ll hand the bank before then. It leads into our RM399 Get Out of Debt plan.

There’s one more thing it does. If your monthly payment doesn’t even cover one month’s interest, the balance never goes down, so instead of running the simulation to its limit and inventing a payoff date, it says so.

Why Astro

The Gatsby site was getting dated and harder to maintain, and we now needed a blog on it as well.

Astro was something I’d wanted to try for the longest time. I’d looked at it back at version 1 and at one point wanted to move the Hargapedia website to it. This was finally the chance, seven major versions later.

Astro sends a page as plain HTML and only loads JavaScript for the components you mark, one at a time. They call these islands. That meant the calculators could come across as React without the rest of the site paying for React, and the blog could be Markdown files in the repo with no CMS to run.

It’s also fast, and good for SEO. Astro’s own 2023 report, using Google’s real-user data, had it as the only framework where more than half the sites passed Core Web Vitals. And in January Cloudflare bought the Astro team and kept the framework open source.

Bringing the calculators across

The plan was to rebuild the calculators last, as React islands, so there was never a point where two frameworks had to coexist.

The first step was to get the maths out of the components and into plain TypeScript files, copied over exactly as it was. Then we wrote tests that check our FV, PV and PMT against Excel’s answers, and pin the old Gatsby site’s results for a set of inputs.

That’s how I know the new site gives the same numbers as the old one. The tests would have told me otherwise.

The first versions of the island UIs were just forms, and I didn’t like them. A calculator should react while you play with it, so they were redesigned the same day. They share a few pieces now: a ringgit field that adds thousands separators as you type while keeping the raw digits underneath, a slider that’s still a native range input so the keyboard works, a result that counts up to its number, and a small confetti we wrote ourselves instead of pulling in a package.

The new retirement result: RM1,382,663 needed by 60, on track for RM1,065,534, and about RM482 a month to save to close the gapThe new overachiever result: RM1,235,388 needed, on track for RM1,400,762 from EPF alone, ahead of the game with an extra RM174 a month to spendFirst Million result: 24 years to the first million, with milestones at year 17, 21, 24 and the second million at year 31Debt-Free with RM10,000 at 18% and RM100 a month: Growing, not shrinking, because RM100 doesn't cover the RM150 of monthly interest
The same results on the new site, then First Million, and Debt-Free when the payment doesn't cover the interest.

What didn’t come over

The old site saved every result and emailed you a report with fund suggestions. None of that exists on the new site. It was built to feed the email, and the email was replaced by a simpler hand-off: a result now ends with a button to carry on with Mama on WhatsApp.

A year and a half in, the calculators are the oldest code we have and the part I’d be most careful changing. But the maths is tested now, the UI is small, and the site around them is a lot lighter than it was.