DynDaCo

MiRa — replacing the system I wrote twenty years earlier

Sector
Membership organisation for active retirees
Period
Original 2000s · replacement 2024–2025
Role
Author of both the original and its replacement
Stack
Ruby on Rails 8.1 · PostgreSQL · Devise · Hotwire · Tailwind CSS

Situation

MiRa is the membership system for a Flemish organisation that brings active retired people together — organised in three tiers: provinces, then federations, then local departments. Volunteers use it to keep member records current as people join, move between departments and lapse; to record who sits on which board at which level and for how long; to manage groups that cut across the hierarchy; and to run a shared calendar of recurring events, categorised and owned by the unit organising them. Every level sees only what it is entitled to see, and every change is recorded.

The name was a deliberate choice and it has done real work. Mira is an old Flemish given name — not an acronym, not a product word. The people running this are retired volunteers, not IT staff, and they were being handed a computer system to look after their organisation's records. A human name in their own language is easier to take to than a set of initials, and twenty years on people talk about Mira the way they would talk about a colleague: what Mira says, whether Mira has the new address yet. That was the point.

I know its constraints unusually well, because I wrote the original. Twenty years ago it was a Perl application running under Apache mod_perl with HTML::Mason templates, and it did its job continuously for two decades.

That is the good news and the problem in one sentence. Software that runs for twenty years earns real trust, and it also quietly accumulates a dependency on a stack fewer and fewer people can safely touch. There was no crisis to justify downtime, no tolerance for losing anything, and two decades of volunteer muscle memory to respect.

What I built

The same system, rebuilt on Rails 8.1, with the hierarchy modelled explicitly rather than implied by queries — provinces containing federations containing departments, with memberships, cross-cutting groups, board terms, and a calendar of events and their occurrences hanging off that tree, all under per-level permissions and an immutable audit trail.

The migration mattered more than the rewrite. Rather than a one-off conversion, the import was built as repeatable tasks: lookup tables first, then core records, pulled from the live legacy database on demand. Because it could be re-run at will, the new system was validated against real production data over and over, and the cutover was a rehearsed operation rather than an event.

The detail I would point to is passwords. The old system stored Unix crypt() hashes, which cannot be reversed. The usual options are to force every member to reset — a support burden, and a poor first impression of the new system — or to carry the weak hashes forward. Instead, a custom authentication strategy verifies the legacy hash at login and immediately rehashes the password with bcrypt, discarding the old value. No member was asked to reset anything, and the weak hashes drained away as people signed in.

The original Dutch URLs were kept, so bookmarks and habits survived the move.

Outcome

The federation runs on a stack its next developer will recognise, and volunteers got a faster, mobile-friendly interface without being asked to relearn their jobs or prove who they were.

Replacing my own twenty-year-old code was a useful exercise in honesty. The decisions that had aged worst were the clever ones; what survived was the boring, explicit modelling of the domain — and, as it turns out, the name. I built the replacement accordingly, on the assumption that someone else will be maintaining it in 2045.

← All work