Nuxt.js is an open-source intuitive web framework based on Vue.js and node.js as well as using powerful development tools such as babel, webpack, and postCSS. Nuxt.js was created by brothers Alexandre Chopin and Sébastien Chopin while open sourcing their code to build Vue.js applications with server-side rendering. The first commit for Nuxt.js on Github was made on October 26th, 2016. Since then the Nuxt.js framework has grown from 2 creators in 2016 to having 10 full-time employees at the NuxtJS company, as well as the amazing Nuxt community with maintainers, ambassadors, and contributors who dedicate their free time to helping improve the ecosystem by creating modules and resources that can be helpful for everyone.
The Nuxtjs.org website which contains the framework documentation translated into 6 different languages has reached more than 200K monthly unique visitors over time.
Time to re-architect our website
Since the creation of the framework, a lot of new features were added. Resources such as modules, themes, and their documentation had increased. We felt the time had come to re-architect our website to make it easier to learn Nuxt and find resources, for all levels from a beginner to an expert developer. We worked on the documentation for almost 3 months and had endless pages in a text editor with all these new docs and code examples.
However, what happens when you work on an ongoing project? Things keep changing. So we were updating our actual documentation as well as our new docs in the text editor. This alone was not ideal and as time went on it was getting tedious to update things in two places. When the engineering team tells you it will be the last major release it doesn’t always mean it will be the last release. This is open source programming after all.
The old stack issue
We couldn’t just replace the new docs with the old ones as the whole documentation had been revamped. Redirecting from the old page to a new one wasn’t a solution because the content had been completely restructured and some pages content had been merged into others. We also couldn’t replace the old docs as we wanted to make sure that developers could still easily find what they needed without having to figure out where things now lived in the new docs. And also we only had the English version complete so we couldn’t remove the old docs as our website supports multiple languages.
Adding a Beta Documentation
Our solution was to create a new link in the header that links to the new docs and in the old docs create an alert to let people know that we have better and improved docs and where to find them. But how our docs were set up was not ideal. The developer experience wasn’t the best and our architecture worked but wasn’t very developer-friendly. It was time to rethink how our content API worked.
The Content module
At the NuxtJS company, we decided to transform our docs repository into a Nuxt module. This is how the Content Module, which allows you to easily add markdown files to your Nuxt.js project as well as use Vue components inside your markdown, was born. This means by adding this module to our website repository, we didn’t need to have 2 separate repos: the website repo for the design and the docs repo for the content API. Perfect!
As the content module was new we needed to test things out to see if it was going to do what we wanted it to do and if not we needed to modify the module for our use case. To give you an idea of how popular this module was it has had 57,000 downloads since its release 3 months ago. Making changes to the module to suit our needs wasn’t an easy task as we had to make sure not to add any breaking changes when adding improvements.
We, therefore, created a proof of concept and keeping the same look and feel as the site we had, we copied over all the site and started deleting things we didn’t need such as the built-in server, the store for the navigation and the markdown plugins, and then error by error started to fix what was broken.
Migrate our data to the new stack
The small job of just adding the new docs was now bigger than we thought. However, it showed us that although there was a bit of work upfront it was going to make not just our lives easier but also those who were contributing to our documentation or writing blog posts on our site. When it comes to contributions the Nuxt.js website has had many contributors over time, as can be seen in the chart below:
Once our proof of concept had been created we decided it was the right decision so we started to move everything to the dev branch of our Nuxtjs.org website and recreate our proof of concept.
Time for improvements
Then it was time to polish things off and improve things we weren’t happy with. For example, we installed the nuxt-i18n module to handle translations instead of custom integration. We also automated some actions by adding needed features to the content module. For example, by using the content module we could automatically create the navigation based on the files we added into the folders. We could then add a menu property into the yaml at the top of the markdown file so that the navigation could be based on the title or if the title was too long we could use the menu property.
We had to make various improvements to the content module so that things could work exactly how we wanted them to, such as being able to search within folder structures for example. This improved the module not just for us but also for all our users. We soon realized that adding this module was seriously making our lives easier as the content module also included a live edit feature which meant we could review in development and make changes directly in the browser by double-clicking the content whenever we spotted a mistake or something that was missing.
Asynchronous development
Of course, it wasn’t just the new docs that we had added using the content module but the whole site. That meant a lot of refactoring of the main website pages plus all the old docs, including all languages, which now had to have a YAML front matter block at the top of each markdown page with the category name and position so it would be positioned correctly in the navigation. Although the only change was the architecture and the addition of the new docs, this was still a tedious task especially seen as we had 6 languages to maintain, but it had to be done.
Now came the real challenge. Pull requests were constantly being made as our documentation is open source. We were now accepting the pull requests on the old docs repo and at the same time adding these minor changes to the markdown files on the dev branch. People were translating our old docs and we were still in the review process of the new docs which made us feel bad as people were working on docs that were soon going to be nonexistent. But at the same time, we couldn’t release the new docs until we were sure that a minimum standard had been achieved and that the site was production-ready.
Continuous code improvement
While working on the docs we saw that our 3 year old project standards didn’t fit anymore, mostly because our focus had been on the framework development. However, since the NuxtJS company got funded, Alexandre and Sébastien Chopin, the founders of Nuxt, built a team of people who could work full-time and make sure all these things were covered. We already had ESLint set up using the nuxt package but that was it. People could still submit a console.log
to the code and it would make it to production, so we needed to fix this.
Lint-staged is a great tool and lints all your staged files and we added linting not just for JavaScript but also for our markdown and styles. We also added Prettier to make sure our code looked the same no matter who had written it. This took quite some time to configure as some of the Prettier rules were clashing with the ESLint rules and to be honest there is still an ongoing discussion as to if we should use both of these packages or not. We also added Husky so we could make sure all this linting was done on pre-commit meaning before someone made a commit these linters would run and alert the developer of any issues before being able to push any changes.
Time to add Tests
Before we were not testing anything on our website. It really was just a small website with documentation so was it really important? It wasn’t until we added a link to our submit newsletter button, so we could better promote it on twitter, did we realise that the submit button wasn’t working after we had updated a module. Nobody had changed the code on our website so how would anyone have known that this was not working. A test would have picked this up. Luckily we discovered it manually just after releasing the tweet and it showed us that it was time to add tests to our website.
For testing we decided to add end-to-end tests and we opted for Cypress.io as it is a great way of testing these types of scenarios, when a user inputs an email address for example. We also wanted to make sure things like our previous and next links worked, our navigation, and anything that involved the user clicking so that we didn’t have to manually test this and could have the confidence that it all worked as it should. We didn’t spend that much time adding tests and it is still an ongoing task as we realized other things could also be tested but we also couldn’t hold off on releasing the docs just because the tests hadn’t been written.
Live it is
Our website was now ready to go live with a whole complete architecture, yet this was just the release of the new docs. We had set the project up with a solid architecture that provides us modularity to maintain and improve our website easily. The only thing we will have to change for the new design is to create new components or modify the styles and possibly some functionality. As we are using Tailwind for our CSS this means we can easily change our colour scheme through the config and then just update the styles when needed. This wasn’t going to be a difficult task. We could easily work on a dev branch and still accept pull requests to our documentation as all texts were now in a content folder meaning it was easy to merge any changes to texts and translations as our code and our content were completely separate.
When we went to merge, the dev branch was 241 commits ahead, 16 commits behind the main branch. We merged it at 7 pm on a Monday as two members of the NuxtJS team had talks to give at conferences and we needed to release the latest version of Nuxt and we couldn’t release it without releasing the new docs. We released and hoped for the best and found a few things that needed fixing but other than that it was all good, or so we thought.
Mistakes we made
In an ideal world, we would go live when everything is ready, when we have implemented a new design, added all the content, and created all the tests. However, that was not the case. We needed to release the new documentation with the new architecture first so that is was easier to maintain. And when we did, we found out a day later that the Japanese version was not working. The language selector was pointing to a different iso code. We hadn’t written the tests for it yet. Luckily we found it as it is very important to us to not break existing translations.
Things we got right
We found that on releasing it was much easier for our translators to work on the new docs. Before you had to clone two repos and run one repo inside another and now you simply have to clone one repo and add the folder for your language, add the .env
variable for the language you want to see and start translating.
Before it wasn’t possible to translate all the pages as it hadn’t been properly set up and there were a lot of markdown files for each component which made it hard to manage. Now we had a language file with all translations for that particular language and by just translating that file all pages such as the home, shop, support, etc would be translated. It also meant that the pull requests were much easier to review as you were just reviewing the i18n file and the content folder for that language when translations were being made. This means that we can now encourage more people to help us translate our docs and also add other languages that were not there before.
We are also working on more content, not just adding documentation but also adding things like a resources section to showcase all the Nuxt modules that people can use to enhance their site. Thanks to our new architecture it is really easy to create new content on a feature branch and when finished, easily merge this new content and release it to the world. When you create a great developer experience it makes you want to improve and add more things to the site and that is a feeling we did not have with the old architecture.
Conclusion
Proof of concepts are a great way of testing out what needs to be done when re-architecting a site. Many people make the mistake of just trying to get it done without first thinking it through and overcoming the problems in the early stages. At the NuxtJS company we built the content module as we needed a better solution for our website. We found that adding linters helps in making sure code is the same across teams as well as helping in finding errors and making sure your code not just looks better but also abides by the correct standards. Adding linters to an already created project is a task in itself but is very much worth doing.
Testing is important and we never have time to test. I still have to add that test for the language selector. When is the right time to add it? When it breaks again? We need to prioritize things better. I guess you could say the job is never done, there are always more things to improve, there is never a finished project and there is no perfect solution. But sometimes you do have to take a step back, look at what you have done, and take pride in the work you have achieved. Then you can set new goals, a new road-map, and perhaps that test that never got written might just make it onto the next list of tasks.
Summary of tools we used
Nuxt.js for framework with target ‘static’
nuxt-content for content
nuxt-i18n for internationalization
Cypress for testing
nuxt-tailwind for our CSS
nuxt-ESLint for linting
Markdownlint for linting markdown
Stylelint for linting styles
Prettier for code formatting
Husky for pre-commit hooks
Lint-staged for linting only staged files
0 comments