Hello button!
Welcome to the 21Buttons engineering blog!
We are glad to start sharing our experiences with all of you and we hope that they can be useful. Don't hesitate to give us your feedback!
We want to launch this blog with one of the biggest tech team concerns: code mantainability and team productivity. We're going to talk about it in detail, but first let's get in context!
21Buttons app
21Buttons is a fashion focused social network and e-commerce application. Our software system is composed by three clients (Android, iOS and Web), and multiple micro-services backing them. They support a lot of features like image posting, user registration, social relationships, multiple searches, notifications, customized content suggester, news feed, products catalog, e-commerce marketplace, and a big etc.
To handle that, this is our main backend stack:
Python using Django as framework,
PostgreSQL as database,
Redis as cache and
Elasticsearch as search engine,
code versioning with Git.
Origin
As an start-up story, it begins with rushes and pushes to the glory. And that often leads to code debt.
A fact that contributes to that is to develop a lot of big features in a short period of time without prioritizing testing nor best practices, from this, refactoring is hard. That increases bug fixing dedicated time and damages the efficiency changing, improving and extending the code base.
Our application was working and scaling with a huge amount of features, users and usage (with all the performance improvements it requires), but with the costs aforementioned.
So, it was time to take a step forward to an agile, mantainable and scalable code base and improve our programming methodologies.
Nothing new here… but let us tell you how we dealt with that!
Challenge
Backend team decided to get down to work and turn this situation around. We had all features working (so all requirements were defined), and we also knew that we need different storage players. Code amount was huge enough, so we finally chose to apply a kind of Clean Architecture keeping Django as main framework. Other best practices and programming methodologies were also added to the recipe to push that process to success.
Clean Architecture
To keep reading, it is also important to be familiar with Clean Architecture.
Here we're going to explain just why we've applied this new architecture (take a look at our post about Clean Architecture in Django where we explain it in detail).
Pros
Our main goal is code readability but we also appreciate code decoupling and collective code ownership (among others). So here are the reasons why this architecture fits that:
- Well defined pieces and responsibilities makes reading, testing and writing clean code easier.
- Standardization helps us to share code between developers.
- Data comes from multiple sources so we need to structure the way we work with them.
- We like Django but we don't like to depend on it completely. Our goal is to be able to easily replace a piece (like Django ORM or Redis, for example), or even the whole framework if needed.
- Integration tests are fine but they have big drawbacks, we need to easy unit testing.
Cons
There are also drawbacks:
- There will be more code, and more code means more problems, always.
- It's not usual to use Clean combined with Django, so developers must learn it.
- Django architecture really differs from clean approach, so merging both is kind of fighting against the framework.
Which we assessed before making the decision:
- The problems that Clean removes overweight those that more code brings.
- Django doesn't put troubles when implementing Clean, and still keeps being really useful.
- Clean is easy to learn, and new developers love it!
Result
We made some different and incremental attempts to apply Clean to some parts of our micro-services. Our approach was to implement the fewer pieces and layers needed, avoiding premature over-engineering.
The challenge was hard, but after a few months working on it we have started reaping the benefits. Now, we can say that we are walking
on the right direction again :)
Take a look at our next post to see how we have implemented that!