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 it dev team.
ChatPilot is a communication aggregator for customer support. You run a business, you have your Facebook fanpage, 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 serveral 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. 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 a Product Owner position, which was mostly focused on UX/UI planning and review.
Before we even started codding the business part of the company alongside 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 how the market looks like. One of discoveries was that one of our partners had customers using and older aggregator that wasn't that good, mostly because messages were slow to show up in the app or didn't showed 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 nice API to work with. The downside is that it's not free and you become vendor locked to given service provider. In some cases using 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 did initially looked at one of communication service provider and business part of the company even talked with them and setup 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 "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 differs a bit so either we would have to handle it or find a library that handles that for us. Such library does exist and is called socket.io.
With socket.io you can create a client in SPA JS web page that connects to a socket.io server written in for example Python. Business agent opens the dashboard, it connects him to the server and awaits on events - like "new message", "new conversation" but also allows him to send events to the server like send reply. 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, 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 lower popularity would with time limit the availability of handy third party solutions and that switching to a more popular framework would allows us to hire developers in the future that know it already. We did some "hello world" type of simple projects in Vue, we checked how 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.
- Flask/JSONApi microservices
- Vue.js dashboard
- Tailwind CSS and Tailwind UI
- Socket.io server
- Google Cloud
- Kubernetes and Docker
- Agile development
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:
- Connect to SIO server and display fixed messages the server would send back on connect event
- Send message, make server send a fixed response.
- Use socket.io room feature allowing two separate connections to "talk" to each other (see each other messages)
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 few times, having to look for vue3 forks (like vue-3-socket.io) and alike. In the end we dropped vue-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 on start won't result with 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 used all of popular Vue extensions from start as well. Early prototype didn't need routing so no vue-router, we didn't even used 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 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 loosing messages after server restart ;)) yet the project moved forward. 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, 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 setup polymorphic relationships between base message model and all message-type models that inherit from it. That way you can do easy timeline/date range select of all messages as well as having unique models for each message type.
The project managed to move to production, got some early customers while the development continues. Things can't break any more (the message model migration had to be planed 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.