It would be a lie to tell you I never used Python before I got to start working on this project. I did the Advent of Code challenge a few years ago purely in Python. That was fun, but I wouldn't exactly call it a good way to learn how to build complex applications.

One advantage I had is that this wasn't the first time I was thrown in the deep. A couple of years ago, I was pulled into an over-engineered TypeScript on the back-end project while never having touched TypeScript before. I thus know this is a stunt I've pulled off before. The big difference now is that this project has a team of experienced engineers. My imposter status could thus be exposed this time.

The ecosystem

The first thing to do on a new project is usually to set up your local environment. The first thing I see in the project is a docker-compose.yml file. Nice, something I'm already familiar with! At a second glance, it only contains a Postgres database, a Redis instance, and an Elasticsearch one. So, where does Python run?

It looks like dockerizing Python projects for local development isn't really a big deal in the community. Instead, you need to have the correct Python version installed on your machine. Then, you would usually create a "virtual environment," which will ensure that your packages are installed in isolation per environment and won't be located and shared in your global Python installation.

Speaking of versions, versioning of Python is done a bit differently than that of JavaScript or PHP. Where JavaScript has extreme backwards compatibility and PHP mostly backwards-compatible changes, this is not the case with Python. Python 2 is still around (after 15+ years of Python 3 being released) because there are so many incompatibilities. This caused the ecosystem to have a much harder time to move forward.

One more thing that confused me was package management in Python. In PHP it's extremely obvious: you just use composer. In JavaScript, you might need to pick between npm and yarn, but at least their config file is quite compatible. In Python, package management can be done in many ways which all have their upsides and downsides. Luckily, I started on a brownfield project, so I did no need to navigate this mess. There was an effort to change a package manager during the last months, but that failed because it caused problems with some operating systems, so they sure aren't compatible with each other.

The syntax

Syntax-wise, I have to say that moving to Python went extremely smoothly. Sure, I have to Google some more than usual, but the entry barrier to reading and writing the code is definitely low. One syntax-wise difference is that Python has the philosophy of only being able to do things in one way. An example would be that you can use either a map or a loop in most languages to convert a list of data, while in Python, you don't have a map functionality out of the box. It is thus sometimes necessary to figure out how to do some things in Python, or depend on external libraries or write custom code to do it "your way".

Paradigms

A bigger burden for me was that the paradigms used in Python are usually much different from what you'd usually see in other backend languages. Even though there are some web frameworks and the language itself has quite some language features as well, the main thing I've seen is procedural scripting. Abstractions are usually limited to extracting functions and potentially placing them in separate files.

In the first team I worked with for this client, I noticed that FastAPI handles routing + documentation for us, and the code usually consists of procedural scripts inside the route method. This felt a lot like what we usually see in audits when inexperienced engineers start out with their first Laravel project but don't have a clue about maintainability.

After about two months of this, though, I moved to another team, and there, we needed to do complex domain modelling. Using Object-Oriented code and Design Patterns such as those found in Domain-Driven Design felt very natural here, but we definitely ran against the boundaries of Python here.

My main frustration has been the lack of private properties, making it basically impossible to encapsulate state in a safe way. You kind of have to trust people to not touch class properties that you've prefixed with an underscore.

One other caveat is how scope works in Python classes. Depending on where you define these in your class, these can be shared between multiple class instances.

Conclusion

Wrapping up, working with Python has been both a learning experience and a challenge. While the ecosystem and paradigms felt unfamiliar initially, they also pushed me to think differently and adapt my approach to development. Python's simplicity and readability helped smooth the transition, but its quirks—such as its package management and lack of certain language features—introduced their own set of hurdles. However, the real takeaway is that, like any new tool or language, persistence and adaptation are the key. Taming the Python is possible—it just takes a bit of patience, trial and error, and, above all, a willingness to learn.