Sendy Firmansyah.
← All work

Case study 03 — Ministry of Defense, Indonesia

ELAPKIN

Joined a 10-month-old, untyped React app mid-project. Moved it to TypeScript and Material UI module by module while the performance-reporting features kept shipping.

Role

Frontend developer · freelance, joined mid-project

Period

2026 — Now

Client

Ministry of Defense, Indonesia

Stack

React 18, TypeScript, Zustand, Material UI, Axios, React Router 7, ApexCharts, Vite

409

JS/JSX files converted to TypeScript

85 → 10

files still on Bootstrap / jQuery

206

typed data-hook files across 43 domains

Context

ELAPKIN is the performance accountability system for Indonesia’s Ministry of Defense: strategic plans, annual performance plans, performance agreements for Echelon I and II units, key performance indicators, quarterly and annual achievement, budget realization, recapitulation and analysis — with print-ready reports for each.

I joined as a freelance frontend developer in August 2026, 10 months into the project.

The hard problem: inheriting the codebase

The app worked, but it was hard to change. It was 409 JavaScript files with no types. The UI was split between React-Bootstrap, jQuery DataTables and hand-rolled markup, across 85 files. Each component guessed at API response shapes on its own, so the same entity was mapped differently on different pages — and field-name drift between them was a steady source of bugs.

Features were still due. Stopping for a rewrite was not an option.

What I did

I chose an order that kept the app shippable at every step.

  • Types first, mechanically. One commit converted the JavaScript modules to TypeScript; a week later the JSX components followed. It wasn’t a rewrite — the point was to put the compiler in the loop. From then on, every module I touched for a feature got strict request and response types, which surfaced the field-name drift where it lived.
  • One data layer per domain. API access moved into typed data hooks — 206 files across 43 domain folders — each exposing fetch, create and update with the same pagination and error handling. All but 2 pages now go through them instead of calling Axios directly.
  • Migrate the UI on the way through. When a feature touched a module, its tables, forms and modals moved to Material UI. Over 2 months that took the codebase from zero MUI files to 150, and Bootstrap and jQuery down from 85 files to 10.
  • Role-based access. Extended route and action permissions to new roles such as unit administrators, and added role-based redirection after login.

Meanwhile the features kept landing: annual achievement reporting, performance agreements, KPI tables, recapitulation, analysis and authenticated document downloads.

Outcome

The codebase I hand over is typed, mostly on one UI system, and organized by domain — so the next change is a local one. The lesson I’d bring to any team with a messy app: you rarely get to stop and rewrite, but you can almost always make every feature leave the code a little more typed and a little more consistent than it was.

Next case study

SARAS