Skip to content

Dental practice/Professional

Keeping FYM useful while planning its replacement

How I took ownership of a decade-old dental-practice system, made deployment repeatable, added recoverable deletion, and planned its replacement

Role
Legacy application maintainer and modernization lead
Period
2025 to present

Inheriting FYM

FYM is the operating system for a dental practice. It connects patient records, appointments, clinical histories, odontograms, X-rays, treatment plans, laboratory work, billing, and staff access. When I inherited it from its previous developer, the application had been largely static for roughly ten years, but the company still depended on it for daily work.

It ran on Symfony 2.8, Doctrine 2, Twig, AdminLTE 2, Bower, and Assetic, with a manually managed dependency tree and PostgreSQL 11. Packages could not be upgraded safely, automatic schema updates were prohibited, and clinical images and financial records made data loss unacceptable.

I had to keep the application reliable while finding a gradual route away from a stack that could no longer be updated normally.

Rebuilding delivery

The first assignment was moving FYM onto a new server setup without interrupting the practice. I reconstructed how the application ran, preserved its frozen dependencies, and configured PHP, Nginx, PostgreSQL, permissions, cache behavior, and asset generation.

I then established a GitHub Actions deployment path adapted to Symfony 2's console layout, manual cache handling, filesystem permissions, and the server's PHP and OPcache behavior. This replaced undocumented server state with a repeatable production-target process. The original cutover and database-transfer logs still need to be recovered before I present the complete migration as independently verified.

This set the rule for later work. Every change had to preserve clinical and administrative continuity.

Recoverable deletion as a system change

Patient deletion showed the risk clearly. A destructive delete was too risky for a record connected to appointments, care history, treatments, payments, and images. Hiding a patient in one screen was also insufficient; the same patient could reappear through search, calendars, reports, or related records.

I introduced a recoverable lifecycle for patient records:

  • deletion and recovery timestamps;
  • the staff member responsible for each action;
  • restricted deletion and recovery actions;
  • a separate view for deleted patients;
  • confirmation and recovery interfaces;
  • functional test cases covering the intended lifecycle.

The initial feature showed how many parts of FYM depended on patient status. Follow-up work added PostgreSQL-compatible migration SQL, registered Doctrine's soft-delete filter, removed deleted patients from ordinary listings, and corrected counts, search, pagination, and care-record queries.

I later applied the same rule to professional selectors, user management, and the treatment catalog. The work defined what an "active" record meant across the application and what happened after someone clicked delete.

Delete request
     |
     v
record marked deleted + actor/time retained
     |
     +----> ordinary queries exclude it
     |
     +----> related appointments and care views tolerate its absence
     |
     +----> restricted recovery view can restore it

Improving daily use

Once deployment and record lifecycle behavior were safer, I worked through smaller problems that affected routine use of the company system.

  • Patient, treatment, laboratory, login, profile, and user-management screens received clearer actions, more consistent tables and forms, and responsive presentation improvements.
  • Appointment views and event feeds were hardened against deleted or missing patients.
  • Argentine currency presentation, input parsing, and financial-value precision were corrected.
  • Unbounded appointment loading was replaced with date-range queries matching the visible calendar interval.

These changes prevent known failures and make daily workflows clearer. I would need usability studies, accounting reconciliation, and performance measurements to claim a quantified effect.

Choosing an incremental migration

There were three broad options:

  1. Keep patching it indefinitely. This protected short-term continuity but left the practice on frozen dependencies and server-specific knowledge.
  2. Rewrite it in one cutover. This offered a clean target, but coupled product redesign, data migration, image transfer, workflow parity, and infrastructure replacement into one high-risk event.
  3. Separate data egress from product replacement. Stabilize the legacy application, expose a read-only migration boundary, verify copies independently, and replace workflows in controlled increments.

I chose the third direction. The final stack is intentionally not locked yet. First I need a trustworthy data inventory, a repeatable copy process, and evidence about which workflows must be preserved. That keeps a framework decision from becoming the migration strategy.

Proving the migration boundary

To test that boundary, I built a local prototype alongside the legacy repository. A standalone TypeScript service uses Hono and an introspected PostgreSQL schema to provide authenticated, read-only access to 29 legacy entities. A serialization layer translates Spanish database names into English API names without changing FYM.

The prototype also inventories patient uploads and returns original image files. A Nuxt explorer makes entity counts, fields, relationships, and paginated records inspectable while keeping the API credential on the server.

Legacy FYM
Symfony 2.8 + PostgreSQL + patient images
              |
              | read-only, authenticated access
              v
Migration API prototype
Hono + field translation + image inventory
              |
              v
Nuxt inspection UI
schema, relationships, counts, sample records
              |
              v
Future verified copy and replacement workflows
target stack still to be determined

This resolves one architectural uncertainty: FYM's data can be inspected through a separate boundary without changing the production application or schema. It does not yet prove a safe migration. Contract tests, safer image handling, checksums, reliable update tracking, deployment, and a sanitized end-to-end copy remain outstanding.

The planned backup interface and SQLite synchronization receiver are design work only. No backup GUI, receiver, replacement clinical application, or production cutover has been delivered through this modernization workspace.

Where the work stands

Legacy delivery

Work: Server compatibility and repeatable deployment. Evidence: Committed, remotely published, and supported by successful production-target automation runs.

Record status and daily workflows

Work: Recoverable deletion, active-record filtering, interface improvements, financial corrections, appointment fixes, and bounded calendar queries. Evidence: Committed and remotely published; production use and measured outcomes still need verification.

Migration boundary

Work: Read-only Hono API, field translation, image inventory, and Nuxt inspection interface. Evidence: Implemented as a local, uncommitted prototype; not delivered or deployed.

Future migration

Work: Backup interface, synchronization receiver, replacement application, and cutover. Evidence: Architecture plans only; none are claimed as built or completed.

Current operation and future replacement

FYM now has a repeatable deployment path, recoverable deletion semantics, safer inactive-record handling, bounded appointment queries, and clearer interfaces. In parallel, the prototype shows how to inspect legacy data without basing every future decision on the old application.

The company can continue using FYM while I test the migration against its real data and operating constraints. Each stage addresses a specific risk instead of deferring all risk to one rewrite.

What the work exposed

The first soft-delete implementation was not the final one. Its original SQL used MySQL-specific syntax despite FYM running on PostgreSQL, and filtering was not initially applied everywhere. Each related workflow exposed another route by which inactive data could return.

The prototype exposed similar gaps: the generated schema has 29 entities while older plans describe 30, several proposed sync fields cannot capture every update, and image transfer needs stronger path validation and streaming.

These findings confirmed why the prototype must remain separate from a migration claim. It exposes unknowns, but it does not prove that data can be transferred safely.

What comes next

The hard part was not writing newer code. I had to find where the old system encoded the practice's operating rules, improve it without losing data, and isolate the parts needed for an eventual replacement.

The next decision is not simply "which framework should replace Symfony?" It is whether the migration boundary can produce a complete, repeatable, and independently verifiable copy. Only then should the replacement stack and workflow-by-workflow cutover be finalized.

This case study remains a draft until my public role, publication permission, production deployment evidence, and sanitized validation material are confirmed.

Technologies

  • Symfony 2.8
  • PHP
  • PostgreSQL 11
  • Doctrine
  • Twig
  • GitHub Actions
  • Hono
  • Nuxt

Skills used

  • Legacy modernization
  • Data lifecycle design
  • Deployment engineering
  • Interface design
  • Migration architecture
WorkAll case studies

More