Packaging-Industry SaaS
Full-stack work on a production web and mobile SaaS for the packaging industry, where growing data had degraded responsiveness for every customer.
Web client, which also served as the backend for the frontend (BFF).
- React
- Next.js
- dev
- staging
- production
Mobile client.
- React Native
- Expo
- dev
- staging
- production
Specific, heavy processing that does not belong in a client.
- Lambda
- dev
- staging
- production
00 / Summary
A contract engagement from December 2024 to December 2025 on a production SaaS for the packaging industry, with web and mobile clients. The current CV reports that more than 10 Japanese companies were using the product during the engagement; the project team was approximately 10 people.
My main work was restoring responsiveness after growing data had slowed the product down for every customer. Alongside it I migrated the Node.js runtime from version 16 to 20 and supported external developers joining the codebase.
01 / Problem
The product had grown past the assumptions it was built on. As stored data accumulated, loading times and general responsiveness degraded across the platform — not for one customer or one screen, but for everyone using it.
The cause sat below the symptoms. The database structure had been put together quickly, before the product was extended to further companies, and its documentation was no longer current. The team did not share an accurate picture of how the existing data model behaved, so changing it safely meant first establishing what it did.
02 / Constraints
- Live customers. Customer companies depended on the system; changes had to reach production without disrupting their work.
- Separate deployments. The web client, mobile client, and backend lived in separate repositories, each with its own Amplify app and its own dev, staging, and production environments. A change to shared data had to stay consistent across all of them.
- No reliable map. With the documentation out of date, the code and the running system were the only sources of truth.
03 / Diagnosis
I rebuilt the picture from evidence rather than from the documentation: reading the code, profiling queries, and following request behaviour in Amazon CloudWatch. Where the code was hard to follow, I drew the data flow out as diagrams before changing anything.
The slowdown was not one defect but several patterns that compound as data grows:
- DynamoDB Scan operations, which read an entire table, where a Query against a key would do.
- Missing global secondary indexes (GSIs), so common access patterns had no efficient path.
- Partition and sort keys that did not match how the data was actually read.
- N+1 request patterns in AppSync resolvers: one request for a list, then one more per item.
- Loading complete datasets to the client instead of only what a screen needs.
- No pagination.
Each of these is tolerable with a small table and one customer. Together, with a growing number of companies, they slowed the whole platform.
04 / Changes
- Indexes designed for the queries search actually runs, and access patterns built around how the product reads its data.
- Pagination, so screens request only what they display.
- A separate search mechanism.
- Caching.
- Resolver changes to remove N+1 patterns.
- Removal of requests the clients did not need to make at all.
- Migration of the job table — the record that carries a packing order through its whole life — to the new structure.
- Timeout work on the paths that export large volumes of data, where the old structure failed most visibly.
When the work was done, I updated the data-model documentation, so that the next change would not have to start from reverse-engineering again.
05 / Delivery
The changes shipped as a series of milestones. Each milestone was verified step by step on staging while engineers on the team reviewed the code in parallel. Production releases ran at night, outside the hours when customers used the system.
The Node.js 16 → 20 migration ran into dependency problems and issues with the Lambda runtime. I verified it with the automated tests and by walking through a predefined workflow by hand: setting up a tenant — one packing company, with its own jobs inside it — and then using the SaaS as that company would.
I also supported external contractors joining the project: reviewing their code, onboarding them, writing documentation, and walking them through their first tasks.
Performance was not the whole engagement. The product follows a packing order from end to end: the job is created, quoted, designed, packed, invoiced, planned into a container, and loaded onto a truck. I built and improved features along that chain — schedule reloading, the quotation, invoice, and packing flows, automatic screen transitions, and notifications — fixed mobile layout and list-navigation problems, and ran proofs of concept for taking the product further onto mobile.
06 / Outcome
After the changes, search timeouts decreased and customer complaints dropped substantially. The product and its adoption are the work of the full team of approximately 10 people; my contribution was the engineering described above.
07 / Retrospective
Two things I would do differently now.
First, I would plan for every change how production returns to its previous state if the release goes wrong. Promoting through dev, staging, and production reduces risk, but it does not guarantee that a production release succeeds, and a data migration is exactly where that gap hurts.
Second, I would map the change’s impact on other components before starting it, not while verifying it. Three separately deployed repositories sharing one data layer make that map worth drawing first.