Ideal Credit Management
The loan management system a consumer credit company runs on every day - rebuilt from Express and MySQL to Next.js and Postgres without stopping the business.
- Role
- Sole engineer, and former credit officer at the same company
- When
- 2021 - now (rebuilt in 2026)
- Status
- Private, used every day
- Next.js 16
- React 19
- TypeScript
- Supabase (Postgres)
- TanStack Table
- Zod
- jsPDF
- Tailwind CSS
- Postgres tables
- 35
- report pages
- 16
- lines of TypeScript
- ~51k
- commits across two versions
- 730+
The problem
A consumer credit company in Moldova needs to know, every day, what each client owes: principal, interest and penalties, per installment. It also has to send the national credit bureau and the regulators their reports on time.
I built the previous version in 2021 on Node.js, Express and MySQL and ran it for five years. In 2026 I rebuilt it on Next.js and Postgres. I worked in credit at this company before I wrote code, so I know the rules from both sides.
What I built
A daily engine that catches up
Every day, interest is added, installments change status, penalties are counted and contracts are re-classified by risk. The job starts from the last day it finished and processes each missed day in order, so a server outage never skips a day. A lock stops two runs at once.
Repairing history, safely
Interest and penalty history can be rebuilt from the schedule and the payments. The planner is idempotent: run it twice and the second run changes nothing.
About a dozen data checks (a closed contract with a balance, duplicate installments, a job running late) feed a report the team can act on.
Splitting a payment correctly
A payment is split into principal, interest and penalty per installment, oldest debt first, with optional reductions. After each payment the contract is checked again and closed and reported when it is fully paid. The same code serves cash payments and payments from other apps.
Reports for regulators
Loan-loss provisions by five risk classes, calculated for any past date. Portfolio-at-risk reports, central-bank and regulator reports, and an XML feed to the national credit bureau. Any contract can be reconstructed as it was on a given day.
Moving five years of data
About 25 migration modules moved the data from MySQL to Postgres, table by table: merging two client tables, fixing wrong legacy dates and moving collateral into its own tables.
The new API kept the old route shapes, so the other apps that talk to the system kept working on the day of the switch.
Trade-offs
- The loan math (repayment schedule, effective annual rate, debt-to-income ratio) lives in my own npm package, ideal-credit. Both versions of the system and the public website use the same code.
- Romanian-only UI, with domain names kept in Romanian in the code. The team and the regulators speak it, and nobody has to translate a term twice.
- Contracts are generated as Word files from templates, because the team edits them by hand before signing.
What I’d do next
Automated tests around the interest and penalty math, so the reconciliation checks become a safety net instead of the first line of defense.
Want to see the code?
I’m happy to walk you through it in an interview.