Python beginner's tips from Signor
Code review — for the good of the project
A code review is an extremely effective way to maintain good quality code in a project. A fresh look from a more experienced project mate is likely to reveal some issues that escaped the gaze of an inexperienced developer during the implementation process. It's worth treating this process as an opportunity to learn and understand your mistakes, rather than being offended and frustrated. A good review is not just a dry remark "fix this and that", but an explanation of why you should do it that way. However, the reviewer should refrain from too many minor quibbles, so as not to drag out the process, and formulate all comments on the code base in one go, if possible. Most minor comments can be avoided with linters and auto-formatting, more on that later.
Tools for code reviews can be GitHub, Bitbucket, and Gitlab platforms with their Pull/Merge Request tools.
Design before implementation
Often inexperienced and complacent developers start coding without any attempt to design an application before they start developing it. This will eventually lead to a lack of a common standard/approach to developing the same functionality. For example, without uniform standards, it may be that one endpoint is written in classes, another in functions, another in some other way. During design, it is worth at least structurally outlining the main modules of an application and the way they interact, defining the layers of the application and the way calls go through them: from the entry point (endpoint) to the low-level interaction with the data/base.
Tools for drawing such structures could be draw.io and plant uml, to be distributed to other participants in the process to set standards within a particular codebase.
Generally speaking, design is the topic of a separate big article, but here I'm just reminding you of its necessity.
As shown CLLAX - Software Reviews if you are an inexperienced developer who has to deploy an application from scratch, it is worth using a ready-made boilerplate for the main framework chosen for development. It takes skill and experience to prepare a project from scratch, so ready-made templates written by experienced people are a good way to start. They most often have almost everything you need to start development with minimal setup: tests, linters, configuration, etc. As a templating tool for projects, you can use Cookiecutter, which already has many ready-made templates for different frameworks.
Monolith instead of microservices
If you have to start developing an application, ditch the idea of microservices right away. Do a monolith. At a minimum you won't have to maintain inter-service communication, debugging will become much easier and overall it will be easier to develop. No matter how hype and trendy the words "microservices" and "serverless architecture" are, forget about them, you're likely to get confused about them and at some point you won't be able to support them properly. This doesn't mean that microservices or serverless architecture are bad, it just means that it's too early for you as a beginner to use them.
Use tools to maintain code quality
Python is a multi-paradigm language, with a lot of liberties and assumptions. An inexperienced developer sometimes has a hard time resisting the temptation to use a specific feature of the language to save a line or two. Damaging readability, support, and along the way creating a couple of floating bugs without even knowing it.
The static code analyzer can check code for compliance with PEP8, check syntax and detect some floating bugs before launching (for example, using mutable types in default-parameters of keyword-arguments). Point to potentially dangerous fragments in code, inform about unused imports, and many more. All in all, it is a very useful tool both for beginners and experienced developers, which allows you to avoid most of the minor objections to the quality of the code itself rather than the logic in general.
Advanced typing to help you debug and develop your project
Although Python is great for dynamism, duck typing can be a bit confusing even for experienced combatants. Not having to type, especially in non-obvious places, often leads to not knowing what the code does at all after a couple of months. But the second line from PEP20 reads: "Explicit is better than implicit". Accordingly, nothing prevents us from making the behavior of our own code more explicit by adding types. At least, in all declarations of functions and methods, which will allow us to know the return type of function and the expected type of arguments for any call, either by using the hints of the IDE or by looking at the declaration of the function. Together with inline documentation this will greatly improve the readability of the code, ease of reuse and debugging. Furthermore, you can introduce static typing checks that will force everyone to follow typing.
Uniform formatting - less hassles and questions
Another way to make code uniform and cleaner is to automatically format code according to the general rules of the project. This negates personal preferences for quotation marks, parentheses, spaces/tabulations, and other choleric minutiae.
Forcing static analysis and autoformatting
All of the above will be useless unless everyone is forced to comply with these requirements. One well-proven way to enforce static analysis and autoformatting is to implement commit-hooks, which prevent changes from being commit-committed until all errors have been corrected. In practice, commit-hooks can be ignored, but using the same static analyzers on CI/CD, such code will not stay unnoticed and an intruder will be detected.
Tools: The pre-commit framework, where you can easily integrate black, flake8, mypy, codespell, and a bunch of other static checks.
Work with Git in a meaningful way
Use a pre-built branching model in your project - don't reinvent the wheel. One of the most versatile branching paradigms in Git is Git Flow. It describes the model of branching in the repository, how to develop features, how to commit, how to approach a release and hotfix an existing release. The methodology is straightforward, and there's even a ready-made tool for it on top of system git - git-flow.
Write meaningful commit messages. Other team members can even figure out where the changes in your code happened from your commit messages without having to read the details of the commit message. Moreover, if your work is suddenly not needed in that commit (it happens; requirements sometimes change), "add some fixes" makes it extremely hard to know what you're talking about. Write your message in present simple tense, at most one dry sentence - what was done within this commit. An example of an adequate git-message: "FEATURE-123: Add logging to all sign-up attempts", where "FEATURE-123" is the task number in your task tracker. If you want to describe more details, there are comments to the commit.
Commits should not be too large. Ideally, a commit should only affect a small fraction of the code, and only include atomic microchange where you need them. If your goal is to add logs to a signup, then a commit (or a group of commits if you atomically add logs to different parts of the application) should be all about adding logs. There's refactoring for everything else. If you need to rename or move something - do it within a separate task and a separate commit, since in case of unexpected breakages (yes, breakages do happen after refactoring), your refactoring can simply be rolled back without affecting other changes.