I thought I was reading my own blog post :) We use very similar tech stack at my current company:
- Ansible for provisioning
- Python/Django for website/api
- VueJS for frontend(where needed, some pages are simple Django templates)
- Celery for background work
- uWSGI and Nginx as servers with AWS Load balancer
- Elasticsearch for search
- Redis for caching
- Postgres with Postgis as main datastore
- Datadog for monitoring
- Cloudflare for DNS
Some differences as I am working with a team:
- We do use multiple branches and git tags for releases. Feature branches are also common as multiple devs maybe working on different features.
- We use Gitlab-CI a lot for testing and auto-deployment(ansible script can be called from our machine as well)
- Terraform for infrastructure provisioning. We have stopped provisioning any AWS service by console. Once the service is provisioned by terraform, ansible takes over.
I have tinkered with Docker, Hashicorp Packer but this setup has been dead simple to reason and scale reasonably well.
How do you feel about using a dynamically typed language like Python for all your backend code? Whenever I had a codebase that grew past several thousand LOC it became pretty unwieldy pretty quickly for me personally. I'm curious if there's a conscious tradeoff for people using Python/Django to start because it's really fast to get up and running with (for existing or new devs).
I don't think typing is the problem you make it out to be. You can specify types in Python, and mypy will type check for you. And nothing about Python means you'll have an unwieldy code base, I can only imagine you'd have the same problems in Java.
The real reason to switch would be performance. The interpreted nature of Python that makes things like Django and PyUnit possible also makes it kinda slow. Some places choose to parcel out a few key services in a faster language (C, Rust, Go) and use bindings to call out as needed.
Completely agree, and I'd like to add that it should only be broken out when performance is actually a measurable issue and there's a good business case for it.
A lot of people get caught up on "x is slow, y is fast" and try to over-architect too early on, then end up focusing on the wrong parts of their product, and sooner or later the project falls apart.
Not OP, however it is fine. I worked in a similar stack as OP with a small team (however it used Ruby/Hanami instead of Python/Django, and of course the difference between both stacks). The code had ~100k LOC for the main service (we also had some other services that had dozen of thousands LOC) and it was completely fine to maintain. Most errors we had wouldn't be avoided by static typed since they're logic errors, not programming errors.
Sure I prefer static languages nowadays since I work in a much bigger company, however I think the benefits of static languages is overhyped.
For us, we follow a very tight convention around how we write code. This has helped us in maintaining the codebase. Of-course some tools help a lot too:
- We use standard tools like flake8, isort, black for code linting.
- We are not following TDD, but we do write tests for all features. Pytest helps here
- Proper code reviews. I cannot stress this enough. As a Tech-Lead I have pushed for code reviews even when working on (supposedly) tight deadlines.
- We are also exploring to use type hints introduced in Python3.7.
PS Shout-out to Poetry(https://poetry.eustace.io/) to make our dependency management easier than ever. Updating third party libs is a lot easier now.
We’re pretty much the same - except for Poetry where we are still using plain old pip. Are you able to say how Poetry makes updating third party libs easier?
There are four popular typecheckers that I'm aware of (mypy [1], pyre [2], pyright [3], pytype [4]), which is a little confusing, but any of the four will catch a lot of type issues that typical statically typed language compilers would catch. It's not as robust as true static typing - there are sometimes false positives and false negatives - but I find it helpful.
Right - I'm totally on board with the overarching goal of keeping the stack as simple as possible, but at the app level, for the best maintenance story, I would want types.
> with any dynamic typed language you have to unit test every single thing possible...
> ... and it becomes not so bad to work with
O_o
How did you manage to put these two statements in a single sentence implying positivity :)
Why do people choose to explicitly write unit tests over and over again for basic things which could be caught by a compiler?
People are losing so much productivity (both short and long term) and being deceived into not having the type system "stand on their way" while they're typing out the initial prototype.
This always bites back when you start first significant refactoring.
I will never understand the appeal of the dynamically typed language. Modern, statically typed languages with strong type systems, provide so much more productivity, reliability and confidence in the codebase that it is really irresponsible to ignore them nowadays.
Did you do the entire stack yourself, I'm planning on using almost identical stack, but need some help, do you have some kind of blog on howto or recommend one
If you create a course on udemy/YouTube on how to setup a production full stack, there are many basic setup videos or tutorials, but none on production full stack with load balancing, replication, caching, etc
- Ansible for provisioning
- Python/Django for website/api
- VueJS for frontend(where needed, some pages are simple Django templates)
- Celery for background work
- uWSGI and Nginx as servers with AWS Load balancer
- Elasticsearch for search
- Redis for caching
- Postgres with Postgis as main datastore
- Datadog for monitoring
- Cloudflare for DNS
Some differences as I am working with a team:
- We do use multiple branches and git tags for releases. Feature branches are also common as multiple devs maybe working on different features.
- We use Gitlab-CI a lot for testing and auto-deployment(ansible script can be called from our machine as well)
- Terraform for infrastructure provisioning. We have stopped provisioning any AWS service by console. Once the service is provisioned by terraform, ansible takes over.
I have tinkered with Docker, Hashicorp Packer but this setup has been dead simple to reason and scale reasonably well.