How I work and best practices
Over the years I've established a certain workflow and best practices, partially taken over from the TYPO3 community and partially created based on the experience with different agencies I worked with.
The tools and principles I use include git, composer, DDEV, frontend prototyping and automated deployments.
Version control system (git)
One of the central pieces is the use of a version control system, preferrably git. I'm familiar with both GitLab with GitLab CI and GitHub with GitHub Actions. I personally prefer GitHub because of their interface and the in my opinion slightly better continuous integration module (GitHub Actions). If I am to control the repository or to introduce git, because the agency does not have any version control system, I use GitHub over GitLab. This point is not negotiable. If you don't already use git and you aren't willing to introduce it into your daily work routine, we are not made for each other. A version control system just makes working with software projects so much easier and lifts your project and your work to a more professional level.
For new projects where I am responsible for the frontend part, I always build a frontend prototype including a defined build workflow following atomic design principles. The frontend prototype allows for easily creating certain variants of element and scenarios like errors when user interaction is involved. Those scenarios and variants are sometimes hard to create in a real world application.
For the TYPO3 CMS itself I always use the composer based installation method. This is recommend by the TYPO3 community, allows for fine control over the installed packages, makes managaging dependencies easy and enables streamlined update procedures and automated update detection. Like the version control system, this is not negotiable.
DDEV / Local development environment
When working on a TYPO3 project I always use a local copy of the website. This allows me to make changes fast, provides proper tools on my local machine and - often really important - allows for using a debugger (XDebug) to find out what happens and why.
Staging / testing environment
Depeding on the size and requirements of the project I use one or two additional environments / copies of the website.
I almost always use a so called staging environment. This is sometimes called testing or QA environment. This is used to give the customer a preview of new features. It is also the place where the customer should test changes or new features there were recently implemented. It may also serve as a playground for the customer. The staging environment usually reflects the most recent code changes and new features, no matter if they are ready for production or not.
If the project requires it, a separate dev or preview environment is used. The dev environment reflects the most recent code and is usually used for internal testing purposes. They customer may of course have access to the dev environment for testing purposes. If a dev environment exists, the staging environment may serve the purposes of a pre-release staging ground. In this case, the staging environment no longer contains the most recent code but only the changes that are declared ready for production either after internal or customer testing.
Which environments are used and for purpose may effect the branching strategy. Please read more on how I use git branches in a TYPO3 project.
One of the biggest sources of error in a software project are the people doing it. This is why I prefer to automate as much as possibe, especially critical parts like how the website transfered to the server.
I use a continuous integration/continuous delivery workflow to automate building and deploying new versions of a project. I'm familiar with both GitLab CI and GitHub Actions (I prefer GitHub Actions though). The project's assets and packages are built as much in parallel as possible using the frontend build proces and composer. After that everything is stitched together and deployed. During the deployment several necessary tasks are executed. This can be updates to the database schema, running upgrade wizards for migrating data or updating the language packs.