meteorjs oss wekan migration

Dev Diary #24 - WeKan Migration Journey, Meteor Core Updates, and Redis-Oplog Fixes

Most of my work this month has been focused on WeKan. WeKan is the poster child for Meteor and I was extremely appalled that it’s still running on version 2. I reached out to Lauri on the official Discord to speak to him because I theorized that the work on 3.0 must have been started but not made public. Yet he told me unfortunately no. 3.0 is such a tremendous undertaking that he wasn’t sure he’d be able to pull it off, and WeKan as is is already sucking up all of his time with no contributors helping around. He did try before to create 3.0 prototypes in the hopes of migrating the existing 2.0 codebase to it, but I recommended against it.

Over my time in the OSS sphere and lately with the 3.0 migration shenanigans, I’ve found myself becoming increasingly aligned with Joel Spolsky’s stance on rewrites – don’t do it!

I decided to take the plunge and dedicate my OSS efforts for the foreseeable future to migrate WeKan. It’d be immensely beneficial for WeKan itself and for the Meteor community as a whole to witness a 3.0 migration out in the wild and how the sauce is made.

Ideally when starting out with 3.0 migrations, you should aim to first:

  1. Get your app to the latest 2.x version, currently 2.16
  2. Prune your Meteor packages. Generally speaking, the fewer Meteor packages you have, the better. This includes:
    1. Replacing Meteor packages with NPM alternatives or outright removing them.
    2. Replacing any Meteor packages with either 3.0-friendly alternatives that are better maintained (e.g., kadira:flow-router to ostrio:flow-router-extra)
  3. Enable WARN_WHEN_USING_OLD_API, introduced in 2.12, and start migrating your server-side app code to async.
  4. Decouple your front end and back end code, and restructure it to allow for separate entry points for client and server. This is a necessity for 3.4 migration.
  5. Flip the switch and migrate to 3.0 and do the necessary grunt work. Certain APIs have been outright removed and you can’t do anything about them in 2.16, so you gotta update to 3.0 and flip the switch!

Steps 2 & 3 don’t necessarily need to be done synchronously. You can do them interchangeably. Migrate a package here and some application code there. So adjust accordingly.

I started my work with the lowest hanging fruit to test the waters, then as I gained confidence I moved onto bigger stuff:

  1. #6067 — Migrate kadira:flow-router to ostrio:flow-router-extra — Replaced the abandoned kadira:flow-router client-side routing package with ostrio:flow-router-extra, its actively maintained successor that supports Meteor 3.0. A very simple change yet a foundational migration step since the router touches nearly every route in the app.
  2. #6072 — Update 2.16 — A broader update PR, likely bumping WeKan’s Meteor version or dependencies to 2.16 as part of the incremental upgrade path toward Meteor 3.0. Thankfully going from 2.x to another 2.x isn’t a challenge since there are no breaking changes.
  3. #6080 — Migrate from percolate:synced-cron to quave:synced-cron — Swapped the unmaintained percolate:synced-cron scheduled job runner for quave:synced-cron, its Meteor 3-compatible fork maintained by the Quave team.
  4. #6082 — Replace mousetrap — Replaced the Mousetrap keyboard shortcut library with a modern NPM alternative. This is a case in point of how you can decrease your Meteor packages while improving the overall health of your app.
  5. #6083 — Remove kadira:dochead — Removed the kadira:dochead package (used for dynamically setting <head> tags like title and meta) and replaced it with a simple document.title.
  6. #6084 — Replace cottz:publish-relations with reywood:publish-composite — Replaced the cottz:publish-relations reactive join publication package with reywood:publish-composite, a more widely used and Meteor 3-compatible package for publishing related/nested documents from multiple collections.
  7. #6086 — Replace mquandalle:collection-mutations with collection helpers — Swapped mquandalle:collection-mutations (which added mutation methods directly to collections) with a collection helpers approach, removing dependency on an unmaintained package. Hopefully with 3.4 to use the native way. ;)
  8. #6087 — Replace ongoworks:speakingurl with limax — Replaced the Meteor-wrapped ongoworks:speakingurl slug generation package with limax.
  9. #6088 — Remove mquandalle:autofocus — Removed the mquandalle:autofocus package, which provided auto-focus behavior for form inputs in Blaze templates, replacing it with native HTML autofocus attribute or a simple manual implementation.
  10. #6093 — Migrate createIndex to createIndexAsync — Converted synchronous createIndex calls on MongoDB collections to their async counterparts (createIndexAsync), which is required by Meteor 3.0’s move to an async-first MongoDB driver. This is what I mean by doing steps 2 and 3 interchangeably. During the package work I shifted focus to do a little app code change then reverted back to packages without interruption whatsoever.
  11. #6095 — Remove idmontie:migrations — Removed the idmontie:migrations package (used for running database schema migrations) and either replaced it with a Meteor 3-compatible alternative or handled migrations differently.
  12. #6096 — List on body for global onRendered — Fixed a Blaze template lifecycle issue, likely moving a global onRendered callback to properly attach to the body/list template level, ensuring correct initialization behavior after Meteor 3 rendering changes.
  13. #6113 — Migrate wekan-accounts-lockout to async API for Meteor 3.0 — Converted WeKan’s account lockout package (which locks accounts after failed login attempts) from synchronous to asynchronous APIs, required by Meteor 3.0’s async-first architecture.
  14. #6120 — Fix async/await in copy/move card operations — Fixed async/await issues in the card copy and move operations, where the migration to Meteor 3’s async MongoDB calls caused unhandled promises.
  15. #6126 — Await async setDone before closing popup in copy/move dialogs — A follow-up fix to #6120, ensuring the setDone callback is properly awaited before the popup dialog closes during card copy/move operations, preventing the UI from closing prematurely before the database operation completes.

The work is still far from over, so keep an eye on the WeKan repo for future PRs ;)


With WeKan out of the way, I also had the privilege to do some work on Meteor itself.

I revived the async hooks PR. It was mostly done but the tests were failing. Hopefully it gets merged in the upcoming 3.4.1, which I anticipate is going to be an amazing release. I’d also like to highlight Italo’s work in spreading the word about Meteor. The amount of PRs rolling in has been increasing, which is something that Meteor direly needs.

Also, the Meteor docs got some love. Ever since the core team decided to move to VitePress and the 2/3 split, things haven’t been super good for the docs, so hopefully that PR gives the little push needed to close out this chapter. Although I theorize that the docs work will never be over because Meteor is always evolving and the changes to track are a moving target. I hope the new LLM core changes can help us in that regard.


Lastly, Redis-oplog got some 3.4 compatibility fixes. I hope Jan can have a look pretty soon.

try my software

Firely - a personal Saas application to track your spending