Kodez
Mar 2021 — Feb 2024
ENTERPRISE CMS · DESIGN SYSTEM & TDD
Client service and logistics teams work in a CMS that loads about 40% faster and keeps working release after release, across integrations with the vendor systems their operation runs on.
Enterprise CMS for service management & logistics, integrating global clients · HQ Melbourne, Australia
Stack
UI · reference build
Jobs
+ NewSingle access point across portal, helpdesk, field and warehouse
Customer Portal
Portal Login
Create Job
Helpdesk
Helpdesk Login
Create Job
Locations
Mobile / Field
Mobile Login
My Parts
Parts Receive
Parts Return
Logistics
Warehouse Login
Shipments
Pick Tasks
Repairs
Open jobs
342
+18 todaySLA breach risk
11
-3In transit
57
Techs on route
24
A reference build of the flow, drawn in code to show the shape of the work. The production app is under NDA.
The pattern, in code
How a component earns its way into the shared library — the promotion rule, expressed as the story file it has to ship with.
// A component is not "done" until every state it can reach is reachable
// in Storybook. This is what made the library adoptable rather than aspirational.
export default { title: "shared/DataTable", component: DataTable } satisfies Meta
export const Default: Story = { args: { rows: sample } }
export const Loading: Story = { args: { rows: [], loading: true } }
export const Empty: Story = { args: { rows: [] } }
export const Errored: Story = { args: { error: new Error("Fetch failed") } }
export const TenThousandRows: Story = { args: { rows: many(10_000) } } // virtualized?
// The gate that made 40+ releases safe: a bug gets a failing test before a fix.
it("keeps a service unbookable when price is missing", () => {
// The type makes this unrepresentable in our code, so the cast is the point:
// this is unvalidated input crossing the boundary, which is what the guard is for.
expect(isBookable({ status: "bookable", price: undefined } as unknown as ApiService)).toBe(false)
})Illustrative — written to show the shape of the decision. Client code is under NDA and none of it appears here.
01 · Context
Kodez builds an enterprise CMS for service management and logistics. The client I worked across was Amtek in Australia, whose operation runs on enterprise vendor systems (Fiserv, Toshiba, NTT DATA, Park Assist and City), so the CMS had to integrate cleanly with all of them. The surface is large — database operations spanning 250+ SQL tables — and it sat alongside a legacy Laravel and jQuery application that still had users. Meanwhile the startup itself grew from 10 to 60+ people. Two forces pull against each other in that setup: the codebase needs to change constantly for clients, and every change is a chance to break something that someone is paid to rely on.
02 · The decision
I architected and delivered the CMS from 0→1 to MVP as React domain modules with SOLID boundaries, then made two investments that most teams defer. First, a Storybook-based reusable component library — the shared spine, so a new screen is composed rather than authored. Second, Test-Driven Development with Cypress, taken seriously enough to reach ~90% automated coverage and wired into CI/CD, so a release is gated by evidence rather than by whoever remembered to click through it. Redux handled state flow, TanStack React Query handled fetching, caching, and synchronization, and Node.js BFF layers cut API latency.
03 · The trade-off I made
Standardizing on the system vs. per-project freedom. A shared library and an enforced test gate slow down the first version of any given screen — engineers occasionally wanted a bespoke component and got a slightly more general one instead. I made that trade deliberately: at 40+ releases and a company scaling past 60, consistency is what keeps velocity from decaying. The migration was the same call in a different costume — I moved the legacy Laravel and jQuery application to React and ExpressJS incrementally rather than in a big-bang rewrite, accepting a period of two coexisting stacks to avoid freezing client delivery.
04 · What I measured
Three numbers, all of them things I could show. Automated test coverage — ~90%, running in CI/CD, which is what made frequent releases safe. Page load time, which came down ~40% through code-splitting, lazy loading, and asset optimization, with AWS S3 serving assets. And frontend development time, which dropped ~30% once the Storybook library was the default way to build. Cross-browser behavior was validated on BrowserStack, and API contracts stayed documented in Swagger, with sprint work tracked in JIRA and Confluence.
05 · Outcome
40+ production releases delivered — new features, defect fixes, and database optimization across 250+ SQL tables. Page loads ~40% faster, frontend development ~30% faster, ~90% automated test coverage, and a legacy Laravel and jQuery application migrated to React and ExpressJS without a delivery freeze. REST integrations against the client's vendor stack (Fiserv, Toshiba, NTT DATA, Park Assist, City) stayed interoperable throughout.
06 · What I'd do differently
What I'd do differently: I sold the component library on consistency when I should have sold it on speed. Engineers adopt a design system when it visibly saves them an afternoon, not when it's described as the right thing to do — I'd publish the before/after build time for a real screen in week one and let the number do the arguing. The lesson I carry: release cadence isn't a function of moving fast, it's a function of how cheaply you can prove you didn't break anything. TDD and Storybook weren't overhead on the 40+ releases; they are what made shipping on that cadence feel safe.
What it moved
Before/after reading
A before/after read around the code-splitting and asset work.
Lab reading, not field data; one contribution among several.
Estimate
My comparison of elapsed delivery on comparable screens before and after the library became the default way to build — at sprint granularity, in a team that was also growing.
Not an instrumented measurement, and I never published a before/after build time for a real screen — which is exactly the mistake the component spec records. Treat it as a judgement of roughly a third, not a figure.
Tool report
The overall number on the merged Kodez coverage report. The suite ran as a merge gate in CI — every critical path had to pass before a merge.
Not one column — branch sits lower. And the suite was gated, the number was not: a change that kept every test green while dropping the percentage would not have been stopped by CI.
Count
Production releases shipped on the Kodez CMS across three years — features, defect fixes and database work.
Engineering practices in play
Tests are how you ship fast
Introduced test-driven development with Jest and Cypress end-to-end gates to ~90% automated coverage inside CI/CD, which is what made 40+ production releases safe rather than merely frequent.
Reusable beats bespoke
Built a Storybook component library as the shared spine of the CMS. I judge it cut frontend delivery time by roughly a third across projects.
Performance is a feature
Cut page load time ~40% with code-splitting, lazy loading, and asset optimization, with assets served from AWS S3.
Migrate incrementally, never freeze
Moved a legacy Laravel and jQuery application I had worked in myself to React and ExpressJS in steps — instead of a big-bang rewrite that would have stopped client delivery.
Each one maps to a specific decision above — the reasoning is on record, not asserted as a trait.