Case study 01

Client modernization · solo product engineering

FAMS ERP

Reconstructing an undocumented fixed-asset workflow for a 40-year-old finance firm, then rebuilding it around traceable physical assets and authoritative financial controls.

Role
Solo product engineer — discovery, architecture, implementation & UAT
Period
May 2025 — February 2026
Repository
Asset-Management-System ↗Private repository
~94%
creator-reported closure across 104 UAT items
10,000
records in exercised test datasets
~20 sec
1,000 procurement records plus 40 related masters
Java 17Spring Boot 3PostgreSQLFlutterDockerJUnit
01 — Business problem

What had to change

The firm relied on an aging .NET and MySQL application with weak search, one-form-at-a-time navigation, limited correction workflows, and no practical way to follow an asset through purchase, custody, depreciation, verification, and disposal.

Requirements were spread across an old executable, incomplete table documentation, operator habits, accounting rules, and organizational memory. My first Flutter prototype modeled the lifecycle incorrectly, which made the real task clear: I had to reconstruct the business system before I could replace its screens.

02 — Decision journey

How the product
reached this state.

The finished architecture makes more sense when the discoveries, reversals, and responsibility shifts are visible.

  1. 01

    Interview the operator, then test the model

    I interviewed the data-entry operator and team, built a one-week lifecycle prototype, and used its rejection to expose gaps that verbal requirements had hidden.

  2. 02

    Use the executable as evidence

    After restoring obsolete dependencies and MySQL tooling, I studied the running system to understand master data, transactions, disabled paths, and the workflow users could not fully articulate.

  3. 03

    Separate ownership, custody, and identity

    Purchase became the ownership event, allocation became custody, and labels gave each physical unit a lifecycle that could diverge from identical units on the same invoice.

  4. 04

    Make calculations inspectable

    Depreciation became a preview, Excel verification, and posting workflow with separate histories for each label and accounting treatment.

  5. 05

    Turn testing reports into a changing specification

    The reports mixed defects, regressions, usability requests, and newly explained business rules. I serialized the decision changes, continued through February, and brought the 104-item backlog to approximately 94% creator-reported closure.

03 — Engineering decisions

Decisions that shaped the system

Each choice connects a business constraint to an implementation boundary and retained evidence.

01

Separate ownership from custody

A purchased asset can remain unallocated, be returned, or move between employees without rewriting the purchase that established ownership.

Evidence · Purchase, allocation, reallocation, and reversal are represented as distinct lifecycle events.
02

Track physical units, not only quantities

A purchase header groups the invoice, line items carry asset economics, and labels identify each physical unit so identical assets can develop different histories.

Evidence · Allocation, depreciation, verification, sale, and scrap operate at label level.
03

Let import absorb dependency complexity

The workbook stays familiar to accountants while the application validates formats, resolves existing masters, creates missing dependencies in order, and batches purchase creation.

Evidence · The benchmark imports approximately 1,000 procurement records and 40 related master records in about 20 seconds.
04

Preview before posting

Calculation, human verification, and financial mutation are separate stages. Multi-year previews remain sequential without forcing users to post each year manually.

Evidence · The product exposes preview, Excel export, per-label histories, and controlled posting.
05

Make Spring Boot authoritative

Flutter requests and presents calculations; the backend resolves rules, lifecycle state, and posted history so results do not depend on a particular client build.

Evidence · Financial calculation and persistence moved from the Flutter client into Spring Boot services.
04 — System model

How the pieces connect

  1. 01Flutter desktop app
  2. 02REST controllers
  3. 03Service-layer rules
  4. 04PostgreSQL records
  5. 05Reports & audit history
05 — Decision models

The logic behind
the interface.

Open any diagram at its original resolution to inspect the complete workflow.

FAMS asset lifecycle model from purchase header and line item to individual label historiesOpen full resolution ↗
01Individual labels connect ownership, custody, depreciation, verification, and disposal without overwriting history.
FAMS Excel import dependency resolution pipelineOpen full resolution ↗
02The interface remains a workbook while the application handles validation, master-data ordering, reconciliation, and controlled creation.
FAMS depreciation preview verification and posting control flowOpen full resolution ↗
03Preview and Excel verification make the calculation inspectable before an approved result becomes financial history.
07 — Implementation and RCA

Where the work
became difficult.

Architecture is only part of the story. These constraints changed how I implemented, tested, and recovered the product.

01

Refactor around a changing domain

Purchase and allocation files grew toward 20,000 lines as product discovery and delivery happened together. Repeated large rewrites failed; smaller AI-assisted extractions reduced entanglement incrementally while preserving behavior.

02

Design for ordinary office hardware

Ordered resolution, DTOs, bounded batches, joined queries, caching, and virtualized tables avoided loading the entire working set or issuing per-row lookups.

03

Treat UAT as an evolving specification

The preserved tracker records 88 solved, eight partially solved, six not solved, and two other entries at its final saved snapshot. Later work brought the 104-item backlog to approximately 94% creator-reported closure, while the historical register remains unchanged.

08 — Outcomes and boundaries

What the work produced

The working system spans purchasing, allocation and reallocation, depreciation, sales and scrapping, physical verification, reporting, audit logs, and supporting master data.

Across a UAT register that mixed defects, regressions, feature requests, and late business-rule changes, I drove approximately 98 of 104 tracked items to closure—about 94%—after the surviving tracker stopped being updated.

Seventeen master-data reports plus the Fixed Asset Register, Fixed Asset Movement Register, Purchase Register, Sale Register, and Scrap Register were reviewed with test data.

Evidence boundary. The 94% figure describes mixed UAT backlog closure, not an automated test pass rate. The surviving tracker directly records the earlier snapshot; the final closure includes later fixes reported by me. I did not retain evidence of client production adoption or regulatory submission, so neither is claimed.

~94%
creator-reported closure across 104 UAT items
10,000
records in exercised test datasets
~20 sec
1,000 procurement records plus 40 related masters
22
reviewed report formats
104
items in the preserved UAT tracker

Evidence basis

What supports this story

Read the full engineering retrospective ↗

AI disclosure. AI tools generated or revised portions of application code, tests, scripts, and documentation within directed implementation workflows and under source review. I owned client discovery, requirements, architecture, module boundaries, prompting direction, source review, debugging, validation evidence, UAT remediation decisions, and release and maintenance decisions.

Next case studyAstroVista