Back

A frontend transformation of a fulfilment platform, migrated module by module with nothing paused

Omnipack Merchant Portal - fulfilments dashboard in the modernised Client Portal

Project goal

To clear years of frontend technical debt from a platform that warehouses run on every day, without freezing the product or interrupting a single shift.

Our scope

Architecture analysis

Migration strategy

UX/UI design

Frontend development

Mentoring & AI training

Project duration

Since 2021

Technologies used

React

React Router v7

TypeScript

Vite

SWR

Client

Omnipack

Five years in, Omnipack handed us the platform the business runs on

We started working with Omnipack in 2021, on their website. That went well enough that when the company decided to tackle something far heavier, its own fulfilment platform, they came to us for that too. Omnipack is one of the largest fulfilment providers in Poland, so the platform is not a side product. It is the thing merchants log into to run their logistics, the thing warehouse staff work in all day. Being trusted with it is the part of this project we are proudest of.

Omnipack Merchant Portal - Omnipack website on desktop and mobile

Two systems, one login apart, with very different problems

The platform has two halves. The Client Portal is what Omnipack's merchants use to manage products, stock, deliveries, orders, returns and settlements. The Backoffice is what the operations and warehouse teams use. It runs to somewhere between twenty and thirty modules covering returns handling, dispatch, packing rules, inventory counts, customer communication and the rest. Both had been built up over years on a Java monolith with Thymeleaf and React mixed together. Both needed the same thing. Neither could be treated the same way.

Omnipack Merchant Portal - Backoffice packing instruction screen

What years of uninterrupted growth had done to the frontend

The platform had been developed continuously for years, with the backend modernised steadily along the way. The frontend grew alongside everything else without a shared architecture or a common set of standards to hold it together, which is how a codebase ends up where this one had: several versions of React running at once, several libraries solving the same problem, several ways of styling a component, no design system, duplicated components and business logic, React tangled into Thymeleaf. Every one of those is survivable on its own. Together they turn shipping a feature into an argument with the codebase.

Omnipack Merchant Portal - before and after of a returns unpacking screen

Some of the system existed only in the code

There was a second problem underneath the first. Documentation was incomplete, so a fair amount of what the platform did had never been written down anywhere. It lived in the code, plus in the habits of the people using it. That meant a rewrite could not start from a specification, because there was not one.

Omnipack Merchant Portal - Backoffice packing workflow across four stages

So every module got taken apart before anything was rebuilt

Each module went through the same treatment. We read the old frontend and the backend behind it, mapped every endpoint a given view actually called, traced the data flows and hunted for the behaviour nobody had documented. Then we talked to the people who use it, because a warehouse operator knows things about a returns screen that no repository will tell you. Out of that came the mockups, the UX improvements worth making and a list of the endpoints that would need to exist. Those went to Omnipack's backend team, where the API changes were designed together.

Omnipack Merchant Portal - returns list in the modernised Backoffice

Two halves, two migration strategies

The Client Portal was rebuilt as one product. Migrating it module by module would have left merchants moving between two different interfaces inside one application, which is a bad experience to hand someone, so the whole new portal was developed in parallel and put behind feature flags. That let the team test safely in production, flip between old and new in a moment, move users across gradually and keep the risk low the whole way. The Backoffice went the opposite way, module by module, because its twenty-odd modules serve different teams and can stand alone. That let us deliver value in the first weeks rather than the first year, take the most painful modules first and retire pieces of the old system as we went. The first new modules were live in production within a month of starting.

Omnipack Merchant Portal - stock levels in the Client Portal on desktop and mobile

The product never stopped shipping

Migrations like this usually arrive with a bargain attached: put the roadmap on hold while we rebuild. We took that off the table at the start. New modules went to production continuously, while new business features were built alongside them, so Omnipack kept getting value the entire time rather than waiting for a migration to finish. A rewrite that pauses the roadmap is a rewrite the business quietly starts to resent. This one never gave anyone the chance.

A new frontend architecture the platform can grow into

The new frontend is a React Router v7 single-page app built on Vite and TypeScript. It is organised feature by feature, so each module lives as its own self-contained slice instead of spilling across the codebase. Everything reaches the backend purely through the API, which draws a clean line between the two sides. That line is what will let the old frontend be switched off entirely once the migration is finished.

Omnipack Merchant Portal - return details on desktop and mobile

A design system, so the platform finally looks like one product

Alongside the architecture we built the design system the platform had never had: colours, typography, design tokens, forms, tables, dialogs, reusable components and the patterns that tie them together. We implemented it on shadcn/ui and Radix, which meant we moved quickly while keeping Omnipack's own visual identity and kept the freedom to extend a component wherever a screen needed something particular. Twenty modules built by different people over several years now look like they belong to the same system, because they do.

Omnipack Merchant Portal - notifications on desktop and mobile

Guardrails so the debt does not quietly come back

Clearing technical debt is easy to undo, so the new codebase defends itself. Linting and formatting are enforced by Biome, types are checked on every commit, unused code is detected automatically and pre-commit hooks run all of it in parallel before anything lands. Conventions are documented in the repository rather than held in someone's head. None of this is glamorous. It is the difference between a clean codebase and a codebase that was clean once.

AI in the daily work, then taught to the client's team

A codebase this size with documentation this thin is exactly where AI earns its keep, so we used Claude throughout: reading unfamiliar code, mapping endpoints, identifying what a module actually did, generating and refactoring code, writing the documentation that had been missing. Then we taught it. We ran mentoring for Omnipack's developers on using these tools in real daily work. The productivity gain landed on both sides, frontend and backend alike.


One useful side effect - plugging our own clients into Omnipack got easy

Something came out of this project that nobody planned for. We build e-commerce stores too, several of which run their fulfilment through Omnipack. Working on the platform itself means we know its API from the inside, so wiring a shop into it is no longer a matter of reading documentation and hoping for the best. We already know what the endpoints do, along with where the awkward parts hide. An integration that normally calls for slow, cautious work now goes in quickly, which our clients get the benefit of without ever knowing why.

Outcome

The Client Portal has been fully modernised. The Backoffice migration is running module by module. The architecture is coherent, the design system is in place, a large part of the technical debt is gone, onboarding a new developer takes a fraction of what it did and features ship faster than they used to. Delivery sped up to the point where the constraint stopped being development at all and became the client's own QA process.

Let's talk about your project idea!

Tell us about your project. We’ll help you plan the architecture, scope, and execution.

Get in touch

© Webalize 2026