We rebuilt ActiveCollab (codename Feather) to make it more user-friendly.
Here’s the technology we used on backend, frontend, our infrastructure and everything else.
We quickly gave up on creating our own framework, because things started to get out of hand and it started to look like we were creating a new Ember or Angular. If these projects already existed and have been proven to work, why create our version in the first place? So the choice came down to Ember or Angular.
After our team made samples of the new interface using both frameworks, Angular turned out as a better solution. It was lighter, had more potential and traction, and our team felt comfortable with it.
On the back-end, we use PHP, MySQL, memcached, Redis, ElasticSearch, and Node.js. The first three components are well known and tested. We have years of experience with this platform, which makes us highly productive.
Stability and familiarity were the main criteria for choosing back-end components. It’s not about “how hip it is” or “I’d lock myself in a room for the whole weekend and play with this framework”. It’s about:
- “Do we have experience with this?”
- “How stable is it?”
- “If we made this a system requirement, would people still be able to self-host ActiveCollab?”
When we look at PHP, MySQL and memcached, we get “Yes” to all three questions. This platform has become better and better as time went on. PHP is much more robust than before (haters gonna hate, leave them — the choice of programming language is as relevant as the shoe size you wear). MySQL is mature and reliable, and memcached is… well, good old memcached.
Stack Elements That Are New
- Redis when we need more than a distributed memory cache,
- ElasticSearch for search and logging,
- Node.js as a persistent web-socket manager for real-time functionality.
We predict Feather will gain traction quickly, and there’ll be plenty of traffic from day one. We need something that’s already proven to work well. Experience from other companies tells us that these technologies are stable and can scale well.
The glue that connects all our front-end components (the main web interface, iOS, and Android apps, browser extensions, and the ActiveCollab Timer is our new and improved API.
We put a lot of work into it, and we think people will like it (we use it every day and it works great). Making a clear distinction between back-end and all the front-ends was great for our team; it made us much more productive — back-end developers are making sure it’s reliable and fast, and front-end developers have a tested and in-depth API, and they don’t care what lies behind it.
Our cloud infrastructure is made up of 8 servers divided into 3 layers: front, app, and storage.
The purpose of the front layer is to make sure that the workload is evenly distributed on the application servers (load-balancing) and that it performs some lighter tasks — like SSL termination, and static files handling.
The app layer is where the ActiveCollab code is executed. These servers are strong and can work under considerable load. In addition to the main application, the memory cache is also distributed in this layer so it would be readily available.
We moved the database on this layer for the same reason. Network latency wasn’t high, and we figured it won’t be, for now. When a request is made to the application server, everything is already on the server and it shortens the execution time considerably.
The storage layer is where we keep all the files that users upload. Servers in this layer have relatively weak processors, but huge discs with hefty caches on RAID controllers.
All the servers have replicas that can jump in if the main server starts to act up. The app layer doesn’t have a backup because it’s elastic — all the servers always work and process some percent of requests. When the load gets over a certain limit, we add new servers to the layer to bring down the load to an acceptable level.
Testing and CI
Some parts of ActiveCollab have been covered by tests for years, but not all. With Feather, we made sure all the main system elements have test coverage, from the internal perspective (unit tests) and external (tests that use API for different usage scenarios simulation).
We use phpunit for unit testing and a tool we created, Narrative, for API testing. The excellent thing about Narrative (and one of the main reasons why we made it) is that it runs the tests and makes developer documentation from the same set of stories. That means the documentation is being written during the development and contains more details than if we left writing for the end. Also, it’s always up-to-date.
Our QA uses Selenium for front-end testing and as a tool for automatization. We use GitFlow because we have to release software in versions (ActiveCollab has a release that users can download and install on their server).
This branching strategy works really well for software like ours, but it may be overkill if you are in SaaS business exclusively and want to have quick and short development cycles. In that case, the GitHub’s workflow may work better for you.
Our CI — Jenkins — is configured to run the whole suite on commit on develop branch. If all tests are a pass, anyone can merge these changes to the master and push them to our staging server.
The code is partially hosted on GitHub (public and legacy projects), and partially on our own server.