We started building Vue Storefront almost 2 years ago. At first, we were sure that our strong knowledge about building software for clients is all we need to build a great tool for developers. We couldn’t have been more wrong.
Most “common truths” are the same. No matter if you’re building software for clients or developers, it turned out that the main focus had to be put on completely different areas than we expected. In this article, I’ll try to make a summary of the most important lessons learned that I wish I had known from the very beginning, to make it easier for everyone facing similar challenges to avoid the mistakes that we made.
PS: You can read more general lessons learned from Piotr Karwatka’s post here. My article is mostly oriented on a developer’s point of view.
TL:DR at the end in summary :)
Know the direction
One of the first mistakes that we made in the very early stages of Vue Storefront’s development was lack of a clear vision of how it should be consumed by developers. We just started coding and adding new features instead of figuring out the way to expose these features for developers. We were unsure how we wanted Vue Storefront to be used by others, and this led to many bad architectural decisions that we needed to get rid of during the next releases.
If we started Vue Storefront today, we would start with clarifying the vision of product usage. It means that you should…
…start with API!
Internals are important but the way you expose it to developers determines how good the framework will be. Your API is a communication layer between code that YOU write and code that YOUR USERS will write.
We started with a very wide API that was tied to the business logic which led to a painful update process. Developers needed to make multiple updates on their applications since the API was changing every time we changed something in our core. It’s very important to think about your API from day zero because it’ll be very hard to change it later. After almost two years we are still getting rid of some poorly-designed parts that are not bulletproof against updates.
It’s good to have this decent level of abstraction giving users the flexibility to use all of your features in the way they want, while at the same time hiding the complexity and architecture so you have the freedom to modify it.
Ideally, you should expose:
- Getters (if your framework provides data) so developers can easily access all the data. In our case, they’re just Vuex getters like getProduct , getCategory , getProductList, etc. Please notice that these getters are oriented around data entities themselves instead of their structure. No matter how much the internals change, these methods will always be responsible for displaying these entities, and they are generic enough to stay the same over time. You can also think about more generic getters (just like firebase’s) if their params are not that entity-specific.
- Actions/Methods that users can perform to access and modify this data (or generally app state). In our case, Vuex actions were good enough to fulfill this need.
- Configuration that determines how the application should behave and set up. The configuration part should allow users to customize how the application behaves (which endpoints it uses, should it work offline, should it calculate prices on the server or client side, should we use Wordpress or Contentful for CMS data, etc). If something can be handled in many ways, it’s good to have the most common option as the default (following the convention over configuration paradigm) but still allow users to pick any other option depending on their needs. Having a config file allows you to provide many customization options without forcing people to write code so it’s a perfect abstraction that hides all of the complexity and can handle updates gracefully.
Once you have a decent level of abstraction, that’ll guarantee that the API is not gonna change that much over time, even if the business logic inside your framework will change drastically, so it’s time to think how the developers will actually use it.
Allow full customization instead of covering every use case
The truth is you can’t predict how your software will be used. You can try to fulfill all the requested needs but this process will never end and keep you busy forever. You’ll also have a bigger and bigger API to maintain.
Instead of giving developers a fish, it’s much better to give them a fishing rod. So instead of making new APIs for every use case, make every meaningful part of your application customizable so users can do it by themselves exactly in the way that they need.
Abstraction is good only if it simplifies things
I write a lot about abstraction — but the truth is that in many cases, it’s a bad decision to introduce it. You should always use the most common approach of handling stuff for the technology stack you use. So in our case, we should use known and common patterns from the Vue community for authentication, routing or managing components. Adding unique abstraction is tempting but it makes the learning process much harder. The more common patterns you use, the easier it’ll be for developers to understand your software.
A good example of an abstraction that can simplify the learning process is something we called Vue Storefront Modules.
Instead of adding extension points for every part of the application (client runtime, server runtime, Vuex, service worker, router, components, etc.) we decided to split our application per feature instead of per resource type. Every module is one feature and should encapsulate all business logic required to make it work.
By using abstraction, we not only made the extending process much easier (you just need to modify the module object to extend/override any of its parts) but also made our business logic much easier to understand since now, 90% of the application is just following the same pattern. If you understand modules, you’ll understand a huge part of Vue Storefront.
Make the entry level as low as possible
No matter how good your solution is, if people can’t learn and use it right after installation, they’ll try to find something easier to use and come back to your solution only if they fail. When developers are choosing tools one of the keys factors is their simplicity.
One of the biggest reasons behind Vue.js success is that it has an extremely fast learning curve. Vue.js is a progressive framework which means you can learn the basics very quickly and do most of the basic stuff only with this knowledge. Once you know the basics, you can progressively widen your knowledge by learning advanced features and exploring the ecosystem which will let you handle less standard use cases and get your job done in a more sophisticated way.
Setting up your framework should be as easy as installing a lib via npm. This is why it’s a good idea to have it as an npm package ;)
If setup requires additional steps for configuration, provide a default setup (remember — convention over configuration) so the user can start playing with it as soon as possible and dig into details later (remember — being progressive).
In Vue Storefront, we have an installer script which handles all the setup steps for you just by asking two questions. Having such an installer allows a user to start playing with Vue Storefront in an instant and after deciding to use it, he/she can just keep playing with a configuration or test out backends other than our demo one. When building a new solution, don’t force any extra steps that are not necessary to try out your solution.
Think twice and solve problems immediately
If you’re building tools that are meant to be used widely, be very careful with bad design decisions that influence your API. Once made, such mistakes cannot be easily reverted and what is more important is that they can lead to much bigger technical debt. Even if you’re in a hurry, when you notice something poorly designed that may influence your future architecture in a bad way, change it immediately.
Split app into self-contained parts
In Vue Storefront we are still getting rid of some bad decisions made in the very early stages. Keeping your architecture modular can lower the impact of such bad decisions. Splitting your app into self-contained parts will also make it much easier for others to contribute because they‘ll need to understand only this small part. This is exactly what Vue.js is doing in its third version.
Ask for feedback and test with community
Things that you perceive as the most important in your software can be very different to what your community and users need. You can create a dedicated communication channel to discuss the roadmap or run voting on Github. Whichever channel is suitable for your community — keep listening to their voice.
Before you launch new changes in the final version, let the community test them and share their opinion as to whether the new features matches their expectations. Mark clearly which API’s are EXPERIMENTAL and can potentially change.
Be careful with updates
Don’t publish too many changes at once. Instead, do your best to list all needed changes and apply them in a test environment first. If update time is too long (in our case we try not to exceed 30 minutes) it might be a good idea to do split it and publish in two parts, automate the process with Node.js script or ESLint plugin.
Be double cautious while making changes in an external API used by extensions. Usually, you won’t be able to communicate with all devs who use it. If you’re launching breaking changes, it’s good to provide an API bridge and leave it for the next few releases. At the same time, keep informing about its depreciation and migration instructions in the console, so developers will have time for a proper migration to new API.
Remember that your users often make their earnings dependent on your solution. Stay as transparent as you can for them, so your users know they can trust you. This is worth investing time in as it is easy to lose trust and a good image, and regaining this trust is very difficult, or even impossible. Whatever you do, put yourself in the place of the most critical user of your software.
Key points of my Vue Storefront lessons learned
Creating Vue Storefront as an open-source framework has been a great adventure that let us build a community of over 800 developers on Slack, and over 30 official partners (learn VS numbers) who all have the significant influence on this solution.
What are the most important things while building open source?
In short, I’d say:
- Think like your greatest critic.
- Take some time to think through your API and save a lot of time and work later.
- Remember, that you’re not the only user of your software and decisions on its future development should be dependent on community needs.
- Treat your community with respect and everything will be good :)
Let me know what your thoughts are about building open source and its best practices in the comments or via twitter @filrakowski.