Younes Ben Salem
All work

The Upgrade Path Was the Product

I spent two and a half years building Oracle JET across 11 release lines. The new components mattered. The path from old to new mattered more.

At Oracle JET, shipping a better component was only half the job. Existing applications still needed a realistic way to adopt it.

JET already had a mature component system built around custom elements and an MVVM architecture. During my time there, its virtual DOM architecture matured, Preact became part of the foundation, the platform moved from Alta toward Redwood, and the first Core Pack components arrived. We were changing frontend infrastructure while teams across Oracle were still building on it.

I contributed to 11 release lines from JET 10.x through 15.x and worked on components including Stepper and Drawer. By the time I left, I had changed my mind about platform work: the migration path is part of the product.

1. A rewrite would move the work downstream

Starting again would have made the new architecture cleaner. But product teams already had working screens built with legacy JET components, Knockout bindings, themes, and tests. A clean break for the platform team would become a rewrite for every team using it.

Core Pack took a different route. Its new Preact-based components used the oj-c-* prefix while the existing oj-* components kept working beside them. A team could move one component or one screen at a time instead of stopping product work for a flag day.

That small namespace difference carried the most important product decision in the new architecture: old and new needed to coexist long enough for adoption to be practical.

2. The Drawer made the boundary visible

I worked on the Drawer family introduced in JET 11. It looked like a panel sliding in from the edge of a page. The product was everything that happened around that panel.

DrawerLayout could reflow the page on a large screen and switch to an overlay on a smaller one. DrawerPopup always used an overlay. The API used start and end rather than left and right so the same component could follow the page’s reading direction.

Then came the questions hidden by the animation. Who owns the open state? When should Escape close it? Where does focus go? Does the content move or get covered? Working on Drawer taught me to describe a component by the decisions it owns, not by the shape it draws.

3. The code had more than one consumer

A JET VComponent was written with TypeScript and Preact, but it could be consumed as a custom element in HTML or as a component in JSX. The build also generated type definitions and component metadata for tools such as Oracle Visual Builder.

So the implementation was not the whole interface. Types had to explain the properties. Metadata had to describe them to tools. Documentation and examples had to show the intended behavior. Keyboard support, translations, themes, and migration guidance all had to agree with the code.

If a component rendered correctly but described itself poorly, it was still broken for someone using it.

4. Compatibility still needed an exit

Running two generations of components side by side was not free. It meant two sets of behavior to understand, more documentation, more audit rules, and a longer period where product teams had to choose between old and new.

JET also made deliberate breaks. JET 11 removed Internet Explorer 11 support. Core Pack supported Redwood instead of Alta, left deprecated APIs behind, and dropped patterns such as the refresh method. The new system needed a destination, not permanent compatibility with every old decision.

The point was never to avoid breaking anything. It was to make each break legible: show the replacement, explain the difference, audit existing code, and let teams choose when to move.

5. Eleven releases changed how I measured shipping

A new component gets the screenshot. The work after that makes it dependable. Its behavior has to survive the next release, the next theme, different reading directions, and applications upgrading from different starting points.

Contributing from JET 10.x through 15.x made that visible. Each release had its own features, but no release existed alone for the teams adopting it. The documentation, audits, and migration steps had to explain the path, not only the destination.

I stopped measuring platform work only by what our team shipped. I started looking at the work an upgrade created for the next team.

Public product references

Build the path with the destination

After two and a half years at the infrastructure layer, I wanted to get closer to the customer and own a complete workflow. That took me to Cisco.

But I carried one lesson with me: a new foundation is not finished when it compiles or even when it ships. It is finished when the existing product has a credible path onto it. For a platform team, that path is not cleanup after the architecture. It is one of the things you are building.