How 10 Engineers Deliver Exactly What the Browser Wants with Qwik

Tobias Zimmermann25 Minuten Lesezeit

Introduction#

Co-Authors of this Case Study: Hristina Staykova, Dino Tudor, Petar Car, Maciej Wienszczak.

Background information on the company and the project#

Unveiling the Dominance#

Sport-Thieme is the number one mail-order company for sports goods. Founded in 1949 by Karl-Heinz Thieme as a one-man business in Grasleben, Germany it has since grown into a global company with over 400 employees and the largest mail-order company for institutional sports in Germany.

Generational Takeover#

In 2014 the management was passed on to the third generation with the responsibility of ensuring sustainable and responsible growth for current and future generations. Despite the expansion, Sport-Thieme remains a dedicated team and family, committed to promoting sports activities focused on a sustainable future.

Unparalleled Purchase Experience from Scratch#

The current shop implementation is a decade old. Back then it was very modern, completely custom and handmade by a handful of developers. There was not a single PHP framework in use. Routing, object mapping and the whole purchase process was written by only two people.

The only library used was in the front end and that was jQuery. At that time this was necessary, because every browser had its own problems and different JavaScript implementations. We used to run on a server architecture called 4th-Dimension as it was perfect for combining web server and catalog production at that time. With the migration to PHP, we also moved our application to the cloud.

Legacy Resilience#

2012 was an odd time to consider AWS and go all in on the cloud. At that time, Germany had not yet embraced the cloud, only years later they started the era of the so-called German-cloud to tell you that it is much more secure. To this day, we are still on AWS and are able to scale even with this older stack, which has a lot to do with PHP's backwards compatibility.

Reasons for considering a switch from PHP/jQuery to Qwik#

In the world of IT, being able to scale up your operations is extremely important. This means being able to handle more tasks, more people, more information, and more team members. However, achieving this can be very challenging.

We changed our IT system to be more flexible with our resources and make it easier to onboard new developers. Our old system was difficult for some developers to work with, which made it hard to run our business smoothly. With the new system, new developers can quickly start contributing to our team. It took us a while to realize we needed to make a change, but we finally asked the right questions and began the process.

Hiring PHP developers#

It was very difficult to hire PHP developers in Germany. When we tried to hire new developers, we weren’t sure if the issue was our budget or if we didn’t look appealing enough on paper.

Finding the right programming language#

We started to contact universities, looked at bootcamps content, and were doing extra research to get an idea of what programming languages were out there to pick from.

Choosing JavaSctipt?#

Some of our developers were already using JavaScript in their personal projects and we noticed that the e-commerce community was starting to use React, Vue, Angular, and other similar tools. At the time, search engine optimization (SEO) wasn’t a top priority and using React for server-side rendering was difficult and didn’t have built-in tools.

We asked a consulting company if JavaScript was a good choice for our future needs. Using one language for both ends would be ideal for full stack development. We also considered using TypeScript, but it wasn’t popular at the time, so we decided against it.

We could not have been more wrong.

Getting started with React and using JavaScript for successful hiring#

After deciding on the new tech stack, we immediately started writing some internal applications with React. It offers a great structure and combined with GraphQL, it was a delight to create internal high-performance single page applications for our colleagues.

Hiring JavaScript developers was easier than we thought, and we had 30 times more applications in our inbox. For the first time we had a lot of great candidates to choose from and ended up hiring more people than we had planned. It felt like the right choice.

How to find the right (meta-)framework#

In 2022, we looked back and learned a lot from our experience with internal applications that used React and GraphQL, including federated graph and code generation. As a result, we felt prepared to use a new stack for our shop.

Looking at the frameworks, and of course all the new shiny meta-frameworks, we were overwhelmed.

In 2022, we realized React wasn’t suitable for our larger multi-page application due to performance issues and lack of server-side rendering support. React’s hydration process caused large bundle sizes and slow initial loading, unless we found workarounds.

What we wished for#

We need server-side rendering as a feature of the framework and not to add another framework or solution on top of the framework.

We want more performance and speed. The single-threaded PHP is not much of an opponent anyway, but we were looking for a reasonable time to first byte as well as more speed to reach interactivity faster.

Like all e-commerce sites, our Lighthouse scores needed improvement due to large CSS files and thread-blocking external JavaScript caused by our old stack. Fixing these issues was almost impossible at this point. Therefore, we needed a new tool that addressed these problems by having simple structures, an easy-to-understand rendering model, scoped styles, good development experience, and a fast hot reload for local development.

The candidates#

NextJS#

NextJS has server-side rendering, comes with an excellent routing, and would fit nicely with a combination of SSR (Server-Side Rendering), SSG (Static Side Generation) + ISR (Incremental Static Regeneration). The API routes can come in handy for certain small APIs the storefront may have. The learning curve for all the NextJS offerings is quite steep. NextJS feels a bit inflexible when it comes to extending the core functionality. You feel a bit locked inside the given workflow of NextJS and with version changes you may run into not-so-easy migrations. Server-side rendered components using the browser API were not available when we looked around. Ultimately, NextJS is a meta-framework for React, and as such shares its pros and cons. We've learned a lot from React, but the web may be moving away from its way of thinking because it has some gaps compared to the other frameworks.

tsx
export function HelloWorld() {
const [name, setName] = useState("world");
return <div>Hello {name}</div>;
}

Svelte#

Svelte is quite easy to use and ticks most of the boxes. Component-based structure, reactivity, server-side rendering ships inside the box. We also liked its history and the fact that it compiles as close to the browser as possible. Because TypeScript was added on top, rather than forced and leveraged from the beginning, it was a showstopper for us.

html
<script>
let name = 'world';
</script>
<div>Hello {name}!</div>

SolidJS#

SolidJS was also quite new, but it looked so much like React that we were extremely interested and played around with it. SolidJS is very lightweight, and the runtime is amazingly fast. TypeScript is included, hooks, suspense, SSR, SSG were also on board. The creator Ryan Carniato is also a guarantee for future success as he knows the challenges of a large code base working for eBay and now Netlify.

tsx
export function HelloWorld() {
const [name, setName] = createSignal("world");
return <div>Hello {name()}</div>;
}

Qwik#

Well, Qwik in one sentence: It is different.

It is very close to the real thing. Anyone who knows HTML, CSS, and JavaScript without a framework will feel right at home. For current React developers, the syntax and usage of JSX provides a
quick setup and easy transition.

It also leverages JavaScript streaming and resumeability. There is no hydration, which leads to high speeds of interactivity. What Qwik delivers is exactly what the browser wants.

We were pleasantly surprised to have a conversation with Miško Hevery, the creator of Angular, when we contacted the Qwik team. After meeting some of the team members at Builder.io, we were confident in our decision to choose Qwik for our project.

tsx
export default component$(() => {
const name = useSignal("world")
return <p>Hello {name.value}</p>;
});

Overview of our PHP/jQuery implementation#

Description of the current PHP/jQuery implementation#

The implementation with PHP and jQuery is almost framework- and library-free. In the front-end, it's only jQuery, jQueryUI, and RequireJS. jQueryUI is only there for its widget factory and accordions, the rest is almost completely written by our team.

RequireJS was added to the stack when we were finally able to use http2. We were also early adopters of SPDY (Google's experimental protocol). RequireJS solved the problem of not shipping all the JS with every single page in the shop, as it only adds files based on the content domain.

In the backend we only use an ORM (Object Relationship Model) as a convenient security layer in the application and of course, SDKs with Composer to talk to external APIs. The rest is handmade by two software engineers, later three, and now maintained by a handful of people on our team.

Advantages of the current implementation#

Let's go back to 2011 when we were working on a custom-programmed shop with a software called 4th dimension, which was a common tool in Germany if you were feeding data to print products and the web.

Soon we decided to go all in with LAMP-Stack (Linux, Apache, MySQL, and PHP) and created a completely handmade shop in PHP.

Because it was handmade, we were able to evolve quickly, frighteningly quickly. Not only did we rebuild the shop with PHP, but we also built a CMS with it and removed a lot of service providers from our stack.

We replaced the search engine and moved to Apache Solr, programmed a custom newsletter tool, and finally a media platform that served as a DMS (document management system) and delivered our images parameterized or by template. We even created our own InDesign plugin and served the CMS data directly to our print catalog.

The LAMP Stack: Reliable and Powerful#

The LAMP stack was already reliable and powered millions of Web sites around the world, so we knew what we were getting.

PHP: A Great Choice with MVC#

PHP was also great, and MVC (Model View Controller) helped us more than ever to understand the data we were working with, because you have to abstract your data into small pieces to work with, and very complex data becomes easier to understand.

Since the world was running on PHP stack then, there were already solutions for everything. We had Memcached from the beginning to keep the database healthy, and as our user base grew, all the tools for our shop evolved as well.

Early Adoption of AWS#

We moved to AWS early on and back then it was not so common to click a button and get computing time just like that. Since PHP is interpreted, it was easy to just move it over, and with only two EC2 instances at the time, we were paying around 15 times less and getting much better
network and computing performance.

Infrastructure Tailored for PHP#

Thanks to WordPress, PHP has grown over the years, so the infrastructure was tailored for this programming language. Along with Java, PHP became one of the go-to languages for building enterprise web applications in Germany.

Challenges with Current PHP Implementation#

PHP is easy to learn, but it’s challenging to master. The language is getting better and better in terms of types, but there are so many inconsistencies in return types and API duplication.

Scalability#

We are far from having scalability issues, but traditional stacks and server-side rendering have some challenges scaling horizontally with extreme loads and keeping performance high.

Security#

Security in IT is becoming increasingly important every year due to incidents worldwide. Since we developed everything ourselves, we are responsible for every aspect of security. Although we have web application firewalls and other security measures outside of our application, using an updated open-source framework that is monitored by AI tools and dependency bots is less prone to errors.

However, it’s difficult to determine the best approach because even frameworks, libraries, and entire applications like Magento or Shopware can be a target for attacks.

Infrastructure complexity#

Vertical growth is difficult, and a shop has many other tasks to handle. Working with PHP and its modules can be painful, and since it’s an interpreted language, bugs may not be discovered until production.

Performance#

Performance is king in e-commerce, and poor performance can be costly. PHP on a machine has some predictability, but not all aspects of it. Therefore, running PHP serverless or scaling it in Kubernetes is not ideal for the web today. Additionally, PHP’s single-threaded nature is a significant limitation for the applications we build at Sport-Thieme.

Hiring#

Recruiting PHP developers is a pain, at least for us, or maybe even widespread in Germany.

Overview of our Qwik implementation#

Description of Qwik and its features#

Qwik solves problems that should not exist in current frameworks anyway. We see no hydration, no duplication of computation, and a broad set of tools to stop thinking in terms of front-end and back-end.

You get filesystem-based routing, SSR, SSG, SPA, reactivity, and much more. The team behind Qwik combined all the great features you can find in other frameworks while also adding new ones you won't find anywhere else. Resumability makes hydration obsolete and is perfect for e-commerce where every byte and millisecond counts.

Advantages of Qwik and our new stack#

What can I say, everything is possible. You can decide where to render, what to render, and when to render. The plugin structure is great, the routing leaves nothing to be desired. You can choose to combine several techniques used side by side.

For example, you have route-based GET/POST functionality and form actions to mutate only the server side, and you can combine APIs with RPC (Remote Procedure Call) like calls with the server$-function.

Qwik can easily be used in a micro-frontend environment as each Qwik-city app is resumable on its own and embedded in another Qwik application. This gives us so many options for serving the site.

We can split the teams to work on specific layouts in their own app and deliver parts of it via CDN, while other parts run on the edge or in containers. We have a good feeling that Qwik can scale in all areas.

In addition to Qwik, we removed the database from the equation. We replaced MySQL with Redis in the stack.

Of course, we didn’t get rid of the database, it's the source of truth for Redis and all shop products can be shipped to Redis in less than 25 seconds. In the future, we will also be shipping product data incrementally to keep it up to date.

Now we have almost no dependencies. This makes it easy to run in local development and we can deploy it anywhere we want. The adapter structure is also great if you want to deploy the same
app in many ways.

Challenges with Qwik and our new stack#

Welcome to the beta#

When we started, Qwik was so new that features were being renamed or removed every week. This wasn't a problem for us as we were in close contact with the Builder.io crew and even had some tiny influence on the naming.

I believe that Qwik’s newness is a disadvantage in terms of community, but the Qwikify implementation makes this a non-issue. You can bring your old React components or libraries over if you want, but I wouldn’t recommend it.

Technical limitations#

As far as technical limitations go, we don't see any. You could argue that some packages or tools are difficult to use with the way Qwik serializes data. But again, I would not call this a drawback as your data can move across boundaries, which is why Qwik exists in the first place, and you should make everything serializable anyway.

How well will Qwik perform#

It is uncertain how Qwik will work in production. We have only done a small set of performance benchmarks with Apache Benchmark, but these are not reliable real-world tests. Seeing the performance in our current implementation and the great SPA navigation in an MPA gives us a very positive feeling that it can scale to infinity if done right.

Planning and preparation#

Getting familiar with Qwik#

What they sell on their homepage is true. If you know React, you know Qwik. We soon started with Storybook, which wasn't that great at the time because Qwik was too new. But after a few days, we had the basic components done and styled.

Data structure#

The most important part was the transfer of knowledge and development experience. We started by writing TypeScript types to get an overview of all the data for a product. The product is the most important part of our shop, and we wanted to have a single type for a product at Sport-Thieme so that the developer can rely on the data everywhere in the new shop.

This was completely different from the current shop, where you have a product type for the list, a type for the product page, and some simplified types for relations or places where you don't want to blow up memory in PHP. This is where Qwik's serialization work shines. No big recursive objects to keep in memory. It's fast arrays, easy to pass, and easy to access.

Data flow#

As mentioned, the database is no longer a touchpoint. This shop should only communicate with Redis and a GraphQL API to place orders and retrieve product metadata like delivery times and shipping costs.

We used data loaders to make the async calls less heavy. Through our experience with GraphQL projects, we learned that loaders can significantly speed up data handling and provide a simple and reliable cache in any JavaScript environment.

Routing#

The first implementation of the shop was planned with a listing page, a product page, and the shopping cart. File-based routing was a fabulous change from our current implementation. We could easily mimic the routes, even if we found it a bit odd to have a folder called /art=[productNumber].

Timeline for the project#

The first milestone period was three months, with weekly sprints and a Friday review. Our plan was to create a listing page, a product page, and a shopping cart. The most challenging part was the detail page, which included many product relations, product ratings, FAQs, and other information. As a B2B shop, we have a unique detail page with a buy box that serves all variants of the product at once. This makes it easy to order large quantities of multiple variants with fewer clicks and faster processing.

In order not to run out of work, we had also planned some bonus tasks, such as CMS-based campaigns and a redesign of the search suggestion module.

Implementation process with Qwik#

The dream team#

We have about 14 people working on the project. This team consists of engineers, architects, DevOps, and a business analyst. Most of us have production experience with React, TypeScript, and Node.js.

Start from scratch#

After much deliberation, we decided to rewrite everything. New backend, new dataflow, new frameworks, new architecture, and a few new concepts.

Atomic design and scoped SASS#

We started with atomic design principles and built from the smallest to the largest. We were fortunate that the number of recurring components is quite small.

For styling, we decided to go back to basics and use SASS with a combination of CSS variables, with the ability to override them in specific contexts, and SASS variables as well as mixins for
spacing, shapes, and color.

Easy to understand data flow#

As mentioned before, we found a very accessible way to use product data to take the development experience to the next level. Wrapping specific types in a Qwik context to reuse a reliable type in the smallest components is just brilliant.

With React, we tend to do a lot of prop drilling or use some overly complicated state management libraries to render only certain parts of your application.

Qwik removes the part of managing complex dataflows to reduce unwanted rerenders through its rendering process. You definitely want to use the tools Qwik provides in your application.

Page by page#

Then we went from page to page through routing, jumping back and forth to find common data flow in the application. This was critical because Qwik provides several ways to write reusable code. For example, you can have common routeLoaders in the layout, create fine granular functions and use the code splitting by the $ sign later, or choose from the other tools like server$, routeAction$, custom API endpoints with onGet, etc.

This helped us create an easy-to-understand project structure. We also decided to have a bunch of context providers to make our lives as developers easier in larger templates like the product details page.

Pick the right tools#

I think the most important part of the project is still to find the best choice of all the tools Qwik offers for each specific part of the application.

We think it's very profound that Qwik has tools for everyone. It doesn't matter if you want to create a rendered static page, a multi-page monster, or a complex single-page application. Even the good old PHP concepts, like doing everything on the server, are an option with Qwik.

Steps to take to go online#

We still need to manage a product delta, which includes static CMS pages, after-sales tools, and customer login, all of which are on our roadmap.

Furthermore, we will modernize the stack to run completely in Kubernetes to have more efficient deployments and a smoother workflow than with our existing strategies. We are particularly interested in leveraging micro frontends and utilizing CDNs (Content Delivery Networks) to achieve optimal performance while delivering personalized content.

Results#

Better Development experience and more fun#

The project structure we came up with and the tools Qwik provided helped us create a product that is understandable, easy to maintain, and fun to work with.

Reducing the number of internal and external dependencies solves a lot of problems. And especially in 2023, there is no need for so much JavaScript anyway.

You have the new HTML5 APIs and all browsers finally talk the same CSS. Nowadays, sliders can be implemented using CSS with just a few lines of JavaScript code.

Better performance of the new implementation#

Qwiks' method of serving pages will end up in a few performance benefits.

Time to first byte#

Disclaimer: We have not yet been able to test this in a production environment, but the takeaway should be that you get consistent performance even as your routes and components grow. We have some edge cases in our product data that are very slow without caching mechanisms due to many product relationships, and we can say that there are no problems in the new setup. They perform almost the same as the products without relationships. Of course, we have changed the way we fetch data, but Qwik also plays a big role in scaling large multi-page applications.

With Apache benchmarks, we tried to get an idea of concurrency and overall performance. To compare the two environments, we used a staging environment of our PHP implementation and turned off the cache. It was expected that Qwik would outperform it even with 0.1 CPUs against a much larger machine. We weren't disappointed, it was over 4 times faster and handled more concurrent requests.

For example, the product detail page in Qwik had 13 requests per second with a time per request (mean) of 1554ms for all 20 concurrent requests.

In the old setup, the same page in our test environment had only 3 requests per second with a time per request (mean) of 7286ms.

But keep in mind that while this is interesting, it's not the real thing, in terms of the network (Berlin is infamous for its internet connection quality) and we've switched to Redis as the data source. But we can't wait to see the real performance with a more powerful system running Qwik and using
micro frontends behind a CDN.

Traffic#

With the SPA navigation through Qwik’s Link-component or useNavigate we saw a 40% decrease in traffic to our page to MPA navigation with PHP. Sure, if you benchmark a single page request, it's almost the same in terms of serving HTML (Qwik is slightly bigger), but adding up the frontend libraries and the different way of serving images, you end up with much more megabytes of data compared to Qwik with SPA navigation.

Development speed#

Not only does the product perform better. It's only a few hours from a design or scribble to a new feature. We were looking for a new search suggestion that changes the suggested products when you hover over the suggested keywords. This was easy and fast to create.

Lessons learned#

Key takeaways from the switch to Qwik#

It was no surprise that the development experience would be great in a new environment. To have the project structure so organized and clear and easy to understand was a delight.

Qwik ultimately has a lot of tools to offer. It is well thought out and you will find a way to do
almost anything with Qwik.

It’s always nice to see how much better a product gets when you start from scratch over and over again. If you stack features for years, they may have technical requirements that you often work around because the setup will not serve you without a bigger change.

For us, this has always been a hard process of profiling larger routes with tons of features. We think Qwik can help with this because of all the different rendering approaches one can implement with Qwik.

Serving the user in the most performant way and serving search engines with server-side rendered HTML is not an easy task.

The development communities have found several ways to do it. SPA is great, and in some cases combined with PWA may be an even better option for our environment if you download the app once and serve API data after that.

For e-commerce, this is a very bad idea and React is trying to figure that out with server components right now. We don't want to wait, because the team at Builder.io has already figured it out and not only made resumability a thing but also helped a lot with Qwik to remove the barriers between server-side and client-side rendering.

Suggestions for other companies considering a switch to Qwik#

Don't hesitate, just try it and give it a chance. If you have a React app, start small and bring parts over with Qwikify, or like us, start from scratch.

If you already know React or SolidJS, nothing stops you. You can get a lot out of it
without changing your mental model too much.

As an e-commerce company, we think all the frameworks will catch up with some of the features in the future, but why wait?

Conclusion and Summary#

Switching to Qwik has resulted in a better development experience and a greater sense of satisfaction for the team. The project structure and tools provided by Qwik have made the product understandable, easy to maintain, and fun to work with. Reducing internal and external dependencies has solved many problems, and with the new HTML5 API and improved CSS compatibility across browsers, there is less need for excessive JavaScript in 2023.

The new implementation with Qwik has shown better performance, including improved time to first byte and handling of concurrent requests. Our benchmarks suggest that our Qwik implementation outperforms the previous PHP implementation without cache, being four times faster and handling more concurrent requests, even on a smaller machine. Qwik's SPA navigation has also resulted in a 40% reduction in traffic compared to PHP's MPA navigation, despite Qwik's slightly larger HTML size.

Development speed has also improved with Qwik, allowing for faster feature implementation and easier building of new features. The organized and clear project structure of the Qwik implementation has been a joy for the team, and Qwik offers a wide range of tools for different rendering approaches.

Switching to Qwik has been a positive experience and the team would not hesitate to recommend it to other companies, whether they are starting small or from scratch. Qwik can be used with existing knowledge of React without changing mental models too much. As engineers of an e-commerce company, the team believes that other frameworks may catch up with some features in the future, but there is no need to wait as Qwik offers a solution now.

Final thoughts#

All things considered, it was a very good experience. We are always looking for the best technical solution and we have high hopes that we have found it in Qwik. The project is still ongoing and we can't wait to continue with the upcoming version 1.0 of Qwik and our new shiny project.