A company wants to start a new web service - a messaging aggregator for customer support. How do you go from an idea to a Python/Vue/Socket.IO web application? This is a story of ChatPilot.io told by its dev team.
ChatPilot is a communication aggregator for customer support. You run a business, you have your Facebook fan page, Instagram profile, maybe even a WhatsApp number - and customers contact you through all those channels. To manage all of such communication companies tend to use aggregators. Those apps aggregate messaging platforms like Facebook Messenger or WhatsApp into a (usually) web application where business employees can manage business-customers communication way more efficiently.
There are apps that aggregate communication, there are even companies providing API/services for making of such applications. The market for such applications is already quite established and the services aren't cheap compared to other online services. If you make your aggregator it quickly will face a lot of matured applications with established brand and customer base. So how and what to do?
The answer would be to make an application that provides value over existing applications. You can't just make an application, it has to be targeted from start at providing unique value and being very agile at adapting to early customer feedback or business opportunities. Easier said than done...
I work for Social WiFi which offers a captive portal service for open WiFi networks. From the code side, it's composed of several microservices and most of them run Flask and offer some sort of API or handle Celery queues. Few like the dashboard are made in Ember.js. The project is deployed to Google Cloud. The initial version of this service, more than 5 years ago, was written as a monolithic Django application that with time started having problems including things like geographic scaling ;)
ChatPilot was planned as an additional service in the company portfolio while also having some synergy with the captive portal service. For this project, we created a 2-man team: CTO - planning, DevOps, code review, and rubber ducky. Fullstack (Python/Fronted) developer - that would be me - to make things done. We also had a sort of Product Owner position, which was mostly focused on UX/UI planning and review.
Before we even started codding the business part of the company our CTO did the research - looked at existing aggregators, also contacted some of our customers that used such apps and after a while, they had some idea what the market looks like. One of the discoveries was that one of our partners had customers using an older aggregator that wasn't that good, mostly because messages were slow to show up in the app or didn't show up at all. Low quality/broken application is something you can compete against, yet it will be unlikely all of your competition has such obvious problems.
To kickstart many applications you can use a service provider that does all of the crude work for you and exposes a nice API to work with. The downside is that it's not free and you become vendor locked to the given service provider. In some cases using a third-party service to bypass some crude part of the application may be worth it - either permanently or for an early version to quickly deliver value built on top of said crude data.
We initially looked at one of the communication service providers and the business part of the company even talked with them and set up a sandbox for development. However, it quickly turned out their pricing won't be feasible for an early version and low-scale customers. So a decision was made to hold them as a future option for bigger volume customers. Such a service for developers would allow us to quickly handle multiple channels but at a price. We had to develop our own support for Messenger and Instagram to be able to launch and scale first.
The goal is to have nearly live communication between the app and communication channels like WhatsApp or Messenger. The app must receive and display incoming messages as they arrive - with as little delay as possible. So how do you get incoming messages? You could pull on a REST API endpoint but that's not very efficient to constantly hit your backend with HTTP requests. Websockets would be the go-to choice although browser support and handling still differ a bit so either we would have to handle it or find a library that handles that for us. Such a library does exist and is called socket.io.
With socket.io you can create a client in the SPA JS web page that connects to a socket.io server written in for example Python. The business agent opens the dashboard, connects him to the server, and awaits events - like new messages, and new conversations but also allows him to send events to the server like sending replies. You can see the basic flow on socket.io original example.
Before starting ChatPilot we used Ember.js for our SPA dashboard but for this project, we looked at other options, like Angular and Vue. Even though Ember worked well for us we wanted to check if more popular platforms offer some advantages. With Ember, we could start developing an application quicker and we would be familiar with all the solutions, and libraries for it. With a new platform we would be going in fresh without experience or knowledge of all the little details.
So Ember or Vue? We feared a bit that Ember's lower popularity would a time limit the availability of handy third-party solutions and that switching to a more popular framework would allow us to hire developers in the future that know it already. We did some hello world type of simple projects in Vue, we checked what socket.io integration looks like and how/if other developers did similar projects to ours. It looked well so we decided to pick Vue over ember for this project.
Project started with a prototype - to showcase the technology and see what are the risks for the project. We started with a very basic Vue project that had to offer a very simple set of features, added progressively:
Such incremental work on the prototype allowed us to validate Socket.io as well as vue.js as both were new technologies for us. We did use Vue 3 which is rather new and not every third-party Vue library supports it. We did encounter this problem a few times, having to look for vue3 forks (like vue-3-socket.io) and alike. In the end, we dropped due-3-socket.io in favor of direct socket.io client usage for extra flexibility (like authentication and so on).
Using a framework for the first time and making a basic prototype at the start won't result in everything being codded and designed optimally or even good from the start. We used Ember but we never used Vue so the change was a learning curve. We didn't use all of the popular Vue extensions from start as well. The early prototype didn't need routing so no vue-router, we didn't even use any storage layer like Vuex - but moved to use Pinia later on.
We started with a simple prototype by working on the list of basic features. As things moved from prototype to intended production feature we had to refactor the codebase quite a lot - reimplementing a placeholder solution with a real one. Like at the start messages were stored by the server in a Python list which then was replaced with a database. From the frontend/prototype side of things nothing changed (aside of not losing messages after server restart ;)) yet the project moved forward. The same change happened in Vue - we used JS arrays but move to Pinia for storage handling and moving such logic from Vue components.
Doing a very dumb early prototype allowed us to start validating the technology but also ideas for the application not to mention early UX/UI work. As this wasn't production level yet we were also allowed to do breaking changes, and create and delete data as needed. We started with prototyping basic features using often dumb placeholder solutions and when that worked for a showcase we proceed with refactoring it into permanent solutions. This created cycles where business saw some new features or a working prototype, then for some time noting changed from the outside when we refactored/reimplemented things to then to be able to start implementing features that would not be possible with the dummy placeholder solutions. This has been communicated to the business - so that so lack of changes doesn't mean nothing is happening.
Even when having permanent solutions those were solutions designed for the early prototype so a lot of things changed with time. For example as we started with simple message model with time we had to start differentiating between customer and agent messages to then handle other special message types. You could start adding optional columns to a table but that's not very clean. So with the help of SQLAlchemy models, we set up polymorphic relationships between the base message model and all message-type models that inherit from it. That way you can do an easy timeline/date range selection of all messages as well as have unique models for each message type.
The project managed to move to production and got some early customers while the development continues. Things can't break anymore (the message model migration had to be planned and had Vue dashboard compatible with both versions) but still we can move forward with the project. As we learn new software stack and as new things becomes needed or would benefit us we implement and refactor and that seems the way to go with such early prototyping/production approach.