case study · client work · regulated health-tech · Latus Group
health-tech-portal/
Yodha is Latus Group’s occupational-health screening platform, and these are its portals: staff, employer clients, employees and field technicians all work in it, and screening devices send their results to it. I joined a long-lived codebase in August 2025 and became its top committer. I built the questionnaire builder, the scheduling diaries and the pipeline that takes in device results, and wrote a large share of its tests.
Nothing here identifies Latus Group’s customers, its staff, its device suppliers or anyone screened. The screenshots are from a local copy with sample data and neutral branding.
scale~3,950 PHP tests548 test files · 10 React apps
dates2025–nowsince August 2025
1 · the problem
Health screening where a lost result is a missed referral.
Employers book screening for their staff: hearing, lung function, vision, hand-arm vibration, skin, questionnaires and safety-critical medicals. Bookings arrive from a legacy back-office system, results come from devices in the field and from clinicians, and then the results are reviewed, escalated where needed and turned into certificates and reports.
The codebase carries three generations of UI, from server-rendered pages to React. Every change has to keep the old paths working while new ones replace them, and nothing a device sends may be silently lost.
2 · what I built
New features in React, a safer intake path, and the tests to prove both.
New screens are React apps built with Vite and mounted inside the existing Laravel layouts, so they replace old pages one at a time. Most of the features below are mine by commit share, and the questionnaire builder was mine from its design spec onwards.
▸The questionnaire builderQuestionnaires are edited as a graph on a canvas, laid out automatically. Each answer routes to the next question and can trigger actions such as a referral or a nurse call. Revisions are immutable: one draft, then publish or roll back, and every submission stays pinned to the revision it was taken on.
▸Questionnaire analyticsPer-question answer analytics pooled across revisions, a side-by-side comparison of two revisions, and JSON import and export.
▸Scheduling diariesA calendar for clinicians and technicians: day, week and month views, recurring appointment series, a next-available-slot search, and conflict and double-booking rules. It replaced the old server-rendered diary.
▸A device-result pipeline that never drops a screeningDevices upload results to signed storage URLs, a queue event starts processing, and a processor per test type takes over. Intake degrades rather than rejects: a malformed field is dropped and logged, and the screening still lands.
▸Device estate and calibrationA register of devices and instruments with calibration compliance and an audit trail, so a result can be traced to a calibrated device.
▸Security and platform workMoved authentication onto the framework’s own auth with role-based permissions, rate limiting and password history; made the API deny by default; upgraded to Laravel 13 and PHP 8.5; fixed portal contrast to WCAG AA.
People in the portals, devices in the field
Select any box to see what it does. Two kinds of traffic meet in one Laravel api: people working in the portals, and devices sending results.
One Laravel 13 app with separate API groups for staff, the portals and devices. Devices are a separate kind of authenticated actor from people. Scheduled jobs handle the back-office sync, reminders and workflow steps.
$ php artisan test --parallel
questionnaire builderRouting answers to the next question in the questionnaire builder.scheduling diaryThe scheduling diary for clinicians and technicians.questionnaire previewPreviewing a draft before it is published.
3 · infrastructure
Kubernetes on AWS, promoted by image, not rebuilt
Select any box. The platform is shared and run with the client’s team; I worked on the application side of it and contributed to the infrastructure code.
infrastructure
AWS · staging and production accountspushwatchprovisionspullsynchttpsfilesSQLcache
selectedKubernetes[KUBERNETES] [HELM]
One Helm chart with separate, autoscaled deployments for the client portal, the admin portal and the device API, plus the queue workers and the scheduled jobs. Staging runs on spot capacity to keep costs down.
$ kubectl get deploy,cronjob
4 · decisions & trade-offs
Every choice had a cost. Here is what it was.
4.1 · Replace screens one at a time
chose
React apps mounted inside the existing Laravel layouts
because
A rewrite of a system people use all day is a long freeze and a risky switch-over. Mounting new apps into old layouts means each screen ships when it is ready and the old one retires the same week.
cost
For a while there are three UI generations to keep consistent, and parity has to be checked screen by screen.
4.2 · Questionnaires as versioned graphs
chose
An immutable revision per publish, submissions pinned to it
because
A questionnaire changes over time, but an answer must always be read against the questions that were actually asked. Pinning each submission to its revision makes old answers safe to read forever.
cost
More data and more care: analytics have to pool across revisions, and drafts need their own lifecycle.
4.3 · Degrade, never reject
chose
Accept the screening, drop and log the bad field
because
Rejecting a whole upload for one malformed value loses a real person’s screening. Keeping the rest and logging the fault means nothing is lost and the fault is visible.
cost
Partial records need clear flags so reviewers know what is missing.
4.4 · Test the whole journey, not just units
chose
A 55-check end-to-end rig against staging
because
The risky failures sit between systems: a booking in the back office, an upload from a device, a review, a bill. The rig books real jobs on staging, sends device envelopes and asserts the outcome in the database.
cost
Not every check can be automated yet: 22 of the 55 run automatically and the rest are a written manual pass.
5 · testing & delivery
Tests first, one image from merge to production.
I added 508 PHP test files over my time on the project, and about half of the 548 in the suite today were first written by me. The React apps have their own Vitest suites, type-checked in CI.
For the risks unit tests cannot see, I built two rigs: an end-to-end plan of 55 checks across six stages, from booking to billing, and a device harness that made an intermittent defect in a vendor’s app reproducible on demand, so it could be fixed rather than guessed at.
$ php artisan test --parallel && pnpm -r test
PHP test methods
~3,950
PHP test files I added
508
front-end tests
~447 in 58 files
end-to-end checks
55 across 6 stages
promotion
same image, staging → prod
code scanning
every pull request
6 · results
$ git shortlog -sn --all | head -1
my commits
2,472 since Aug 2025
rank by commits
#1 of the team
questionnaire builder
designed and built
scheduling diaries
built, old page retired
result processors
7 test types
React apps in the workspace
10
portal contrast
WCAG AA
framework
Laravel 13 · PHP 8.5
A system people rely on, and a codebase that has to keep moving?