Skip to content
shipped

University of Jena: Vue 3 Migration

Migrating nine physics-faculty institute sites from Vue 2 to Vue 3, and making the shared component library the standard again.

Nine institute sites ran on a Vue 2 codebase nearing end of life, with a shared component library that had drifted into inconsistent, chaotic use.

role:
Frontend developer; drove the migration strategy, testing, and component library adoption
stack:
Vue 3 · Vuex · Cypress · Webpack · Axios · SCSS · Tailwind CSS
when:
2019–2022
published:
University of Jena: architecture and migration path A shared component library feeds nine institute sites, multi-page Vue applications built with Vuex and Webpack, which call the REST APIs of a Java backend through Axios. Below, the migration lane: the Vue 2 production codebase stays live while a separate migration repository converts it to Vue 3 incrementally under Cypress tests, and the result is released to production only once tested. Shared component library 9 institute sites (PAF) multi-page Vue apps Vuex · Webpack Axios Java backend REST APIs migration lane Vue 2 production stays live Migration repository incremental Vue 3 conversion Cypress tests Release once tested
Nine institute sites share one Vue component library and consume REST APIs from a Java backend. The Vue 3 migration ran in a parallel repository and replaced production only after full testing. · scroll sideways to see all of it

Problem & constraints

Friedrich-Schiller-Universität Jena’s web presence spans more than a hundred departments. My slice of it was the Physikalisch-Astronomische Fakultät: nine institute websites, from the Astrophysical Institute and University Observatory to the Otto Schott Institute of Materials Research and the Fraunhofer Institute for Applied Optics and Precision Engineering (IOF).

The sites ran on Vue 2, which was approaching end of life, and every year of growth made the codebase harder to maintain. The framework wasn’t the only problem: a shared component library existed, but adoption across the sites was inconsistent (some teams used it, some didn’t), so it had drifted into an obsolete, chaotic state with per-site variants of the same UI.

Two constraints shaped everything. The sites had to stay live throughout; a university can’t take its institutes offline for a rewrite. And this was a cross-functional team (backend developers, UI/UX designers, DevOps engineers), so the migration had to move without blocking anyone else’s work. The Vue migration itself, its testing, and the component library were my responsibility.

Architecture

These were not SPAs. Each institute site was a multi-page application with Vue mounted where interactivity was needed, on dashboards and interactive components, built with Webpack. State lived in Vuex, and data came over RESTful APIs (via Axios) from a plain Java backend. The shared component library sat across all nine sites.

The migration itself ran in a separate repository: the codebase was converted there, incrementally and under test, while production stayed untouched. Only once the converted code was thoroughly tested did it replace the live sites; there was never a cutover of a half-migrated codebase. The decisions behind it are logged below, each with the alternative it beat.

Outcomes

Tree-shaking and Vue 3’s smaller runtime cut the JavaScript bundle by 40%. Accessibility improved to WCAG compliance through ARIA attributes and semantic HTML, and Cypress-based integration tests, introduced together with the team, gave the migration end-to-end coverage.

Just as important were the structural wins: the platform could keep up with current Vue features again, old hand-rolled logic was replaced with well-supported Vue ecosystem addons, the component library was consistent and current across all nine sites, and the codebase came out of the migration noticeably cleaner than it went in.

Lessons

If I did it again, I would convert everything to the Composition API and leave no Options API behind. Keeping the stable sections felt pragmatic at the time, but a mixed-style codebase carries a quiet cost: every future upgrade and every new contributor has to deal with two mental models, and the leftover Options code is exactly where future friction will surface. The incremental, separate-repo strategy, on the other hand, I would repeat without hesitation: it’s what let nine live university sites migrate frameworks with no drama.

Key decisions

  1. 01 Migration strategy

    Incremental conversion in a separate repository.

    Nine public institute sites had to stay online, so a parallel repo could be converted and tested piece by piece, and only replace production once it was proven.

    The live sites never served half-migrated code; the swap happened once, after the converted codebase had been thoroughly tested.

    rejected: A big-bang rewrite on the live codebase

  2. 02 Options API vs Composition API

    Composition API as the default, keeping Options API where it still worked fine.

    Most of the logic genuinely gained modularity from the rewrite, but stable Options-API sections weren't worth the extra churn within the migration's scope.

    This is also my biggest lesson from the project; see Lessons above.

    rejected: Forcing a wholesale conversion of every component

  3. 03 State management

    Fixing the existing Vuex implementation.

    Vuex was already wired in but used incorrectly, and correcting the store patterns recovered its value without stacking a second migration on top of the framework one.

    rejected: Ripping it out for a different store

  4. 04 Shared component library

    Reviving the shared library and making it the default across all nine sites.

    Inconsistent adoption was exactly what had made the library obsolete and chaotic; consolidating meant one place to update and a consistent UI everywhere.

    rejected: Letting each site keep its local component variants

Key figures

Key figures (self-reported)
MetricValueStatus
Institute sites migrated9reported
JS bundle size−40%reported

Reported: The faculty's institute sites in the migration's scope, 2019–2022 · Production JS bundle from the build output, before vs after the Vue 3 cutover