What I've learned after 8 years of Python
The Zen of Python with examples, my recommendations and other tips & tricks!
In this practical article, I’ll lay out a set of best practices that have helped me fine-tune the way I code in Python.
I didn’t learn about these from the get-go, but as I advanced in my career, they became obvious, and I learned them indirectly as I wrote code and studied best software principles and clean code concepts in Python.
Let’s start with a few interesting charts!
Python Popularity

Even before the AI field grew so quickly, Python was a popular language among developers. It’s got a clear and easy-to-understand syntax that allows people of all experience levels to learn quickly and be productive.
It’s portable due to the Python Interpreter, so cross-platform development doesn’t involve compiler versions or any other roadblock.
With the surge of AI, Python became even more popular with big teams such as the Tensorflow team at Google and the PyTorch team at Meta choosing a Python-first approach. A fast-moving field requires a streamlined approach, and Python was perfect for that.
Further, many other tools, open-source frameworks, and libraries took on the initiative and were built with Python-first in mind. Let’s see a few examples:
Pandas
PyTorch (C++, Python)
Tensorflow, Keras (C++, Python)
Polars (backend in Rust, API in Python)
Bytewax (backend in Rust, API in Python)
HuggingFace Transformers
OpenAI GPU Triton Lang
Langchain, Langgraph, vLLM, SGLang, TRTLLM
and the list goes on…
With more companies building with AI, it only makes sense to look for experienced developers in what powers AI, and that includes proficiency with Python. Here’s another chart from the 2024 Dev Survey on StackOverflow, doubling down on the mentions above.
Having established that Python is one of the hottest languages right now, I want to iterate on a few principles I’ve learned throughout my career and that have helped me a lot.
The Zen of Python
Want an easter egg?
In your Python terminal, type in `import this` and you’ll see the Zen of Python by Tim Peters.
The Zen of Python is a collection of 19 "guiding principles" for writing computer programs that influence the design of the Python programming language. Python code that aligns with these principles is often referred to as "Pythonic".
Here’s what it says:
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one-- obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
*The 20th one should have been written by Guido, the Python language creator,
but was never completed*
Let’s unpack them step by step.
Beautiful and Explicit
Multiple concepts go into this one. Starting from how you name your variables, how you split the code block’s logic, how you write docstrings, pass arguments, bound arguments to a type, and so on.
Make your code explicit. If functionality happens inside a class, make it crystal clear what the method does and what variables are updated.
Simple is better than Complex, better than Complicated
Over-engineering is a concept all developers have in their minds, and we all have done it. Programming is an art, and we want it to look good, but we shouldn’t miss the fact that the code’s purpose is to run and fix a problem.
Of course, having Inheritance, nested blocks, and complex classes looks nice and might signal “Hey all, this is great design.”, but a functionality should be robust and not need tons of layers on top of it.
Keep things simple, avoid over-engineering!
Readability, Flat is Better than Nested
Python is a dynamically typed language and allows you to do things quickly. When you write Python code, you should aim to isolate the logic for each method and code block, making it readable first, even if it takes multiple lines. It’s true, some functionality might be done in a one-liner, but the fewer lines you write, the more errors you might get.
Plus, keep in mind that when you’re working in a team, other people will read your code, and you don’t want a rain of comments on your PR. Other people don’t have your context for that code block, so they should understand it simply and quickly.
Errors should not pass silently!
When you write Python code, always aim to use built-in exceptions to treat edge cases and treat exceptions accordingly. If you’re reading from a file, don’t assume that the file always exists. Add a FileNotFoundException to handle a file that is missing.
Moreover, if you’re parsing a JSON or any other object, use ValueError to handle missing keys or bad parsing. The list can go on, but what’s important is that you should aim to catch and treat possible exceptions your code might encounter.
If you don’t have a specific logic for any exception, log it using logger.error, to have that message in your CLI or .log file.
Use namespacing!
This is a key one I’ve seen in many open-source projects on GitHub. The rule is to avoid star (from X import *) imports or multi-module imports where some methods might have conflicting names. Use namespaces instead.
Instead of importing specific methods, import the module and reference methods from inside that module only. Here are a few more examples for that:
import utils
result = utils.my_func()
import tools
result = tools.my_func()
A great book recommendation I have on al these topics is Fluent Python!
Environments and Package Managers
In Python development, managing project dependencies and Python versions efficiently is mandatory. Virtual Environments provide isolated spaces for each project, preventing conflicts between different project requirements.
For installing, updating, and removing libraries from these environments, we’d use a Python Package Manager. For the longest time, the common ways to do that were:
Virtualenv and pip install requirements.txt.
Poetry and Pyproject.toml
Conda
I’ve been using all of them or a variation since the first year I started working with Python. But, currently, I’ve switched to the newest addition - uv, and have never looked back.
The uv tool is a high-speed package and project manager for Python. It’s written in Rust and it does everything all the above solutions did, but way faster. It offers fast dependency installation and integrates various functionalities into a single tool.
You can install it with: curl -LsSf https://astral.sh/uv/install.sh | sh
Getting Started with UV
There are 3 steps to follow when starting:
Create a new project with `uv init <name>`
Initialize a new VirtualEnvironment with `cd <name> && uv init`
Start adding packages to your Env with `uv add <pkg_name>`
And that’s it, now you can activate the virtual environment and work using the specific Interpreter.
cd <name>
source .venv/bin/activate
uv sync
Getting more out of UV
Below is a list of commands and features UV offers, across many use-cases, from using it inside a Dockerfile, to building a distributable wheel (.whl) file for your package, or separating dependency groups in [dev], [prod], or test.
Extra Recommendations
For a few extras, I recommend using static typing, Python built-in flags, and Dataclasses or Pydantic Models for schema validations.
Static Typing
Instead of this, which is prone to errors if the data argument is not a Dict:
def process_data(data):
for item in data:
name = item['name']
value = item['value'] * 2
print(f"{name}: {value}")
Do this, where you specify types and expected schemas:
from typing import List, TypedDict
class DataItem(TypedDict):
name: str
value: int
def process_data(data: List[DataItem]) -> None:
for item in data:
name: str = item['name']
value: int = item['value'] * 2
print(f"{name}: {value}")
my_data = [
{'name': 'A', 'value': 5},
{'name': 'B', 'value': 10}
]
process_data(my_data)
Even more, Pydantic Schema Validations
The code example above can be improved one more step by using Pydantic Models as validation schemas, thus instead of this:
from typing import List, TypedDict
class DataItem(TypedDict):
name: str
value: int
def process_data(data: List[DataItem]) -> None:
for item in data:
name: str = item['name']
value: int = item['value'] * 2
print(f"{name}: {value}")
my_data = [{'name': 'A', 'value': 5}, {'name': 'B', 'value': 10}]
process_data(my_data)
Do this, where we validate DataItem against a BaseModel in Pydantic
from typing import List
from pydantic import BaseModel, field_validator
class DataItem(BaseModel):
name: str
value: int
@field_validator("name")
def val_name(v: str):
if v in ["A", "B"]:
return v
else:
return "NONE"
def process_data(data: List[DataItem]) -> None:
for item in data:
name: str = item.name
value: int = item.value * 2
print(f"{name}: {value}")
my_data = [
DataItem(name="A", value=5),
DataItem(name="B", value=10)
]
process_data(my_data)
So here, we’ve used a BaseModel with a field_validator saying that a DataItem will be valid for A and B, because any other value for name will transform the name field into “NONE”.
Of course, this is a basic example, but it illustrates the point as with Pydantic models we don’t just emphasize typing, but can also add validations, field descriptions, default values and more.
Final Thoughts
The list described in this article touches only a few points, as there are multiple guides out there on how to write clean Python code.
I wanted to keep it short and practical, providing code samples to illustrate each topic.
On that note, I have two more advanced walkthroughs on Python for you that I think you’ll enjoy and will learn a lot from.
Learn to manage configurations in Python (Expert Insight)
Learn how Python works internally (Expert Insights)
Thanks for reading this article!
See you next week ;)
A great read. Very detailed and good part is it has code examples to show. 💯
Great article Alex!