A new TYPO3 project can be a great challenge. And of course the claim is like always: "this time everything will be perfect". Well, what is perfect? We certainly don't know. But sometimes we understand that doing B is better than doing A. And we never do A again.

This post is about technologies, workflows, some Dos and Don'ts and its goal is to lead to better setups overall. Hopefully you find at least one B to replace one of your A's. But enough with the confusion! As usual this list is not complete and I am for sure not aware of every good practice. So please contact me for improvements, additions or corrections. Very much appreciated!

Use Composer

TYPO3 is moving towards fully embracing Composer for dependency managing and autoloading. And so should you in your TYPO3 project. Have a look at my post TYPO3 and composer to get started. Let's break Composer usage down to the most essential implications for a new project:

  • The web folder: Separate your project root folder from your document root. The project root is the folder your project lives in. It is where you have your .git folder and also your composer.json and composer.lock files as well as the vendor folder where Composer downloads all packages into. The document root is the folder your webserver points the web requests to. It is where your index.php and the folders fileadmin, typo3, typo3conf, typo3temp, uploads are located. This folder is usually called web but can be named differently if required.
    By separating both roots, files in your project root are no longer accessible by web requests (unless they are symlinked to the document root) and you don't have to worry about protecting them with .htaccess rules any more.
  • TER extensions. TER extensions are added, updated and removed exclusively by being required with Composer. They are no longer downloaded, updated or removed in the extension manager, in fact those options are not even available in the extension manager when TYPO3 was set up with Composer (TYPO3 will run in Composer mode). Note that extensions still need to be installed and uninstalled in the extension manager nevertheless.
  • Autoloading. All classes of the project are loaded by Composer. No exceptions. If you have extensions that are manually placed in web/typo3conf/ext and were not downloaded by Composer you have to add the autoloading information for classes in those extensions to your root composer.json.

The composer.json of a TYPO3 project could look like this. The project uses TYPO3 7 LTS as well as the extension news from the TER and a custom etensions named my_extension:

  "repositories": [
    { "type": "composer", "url": "https://composer.typo3.org/" }
  "name": "DanielGoerz/composer-example",
  "description" : "A TYPO3 7 LTS example project with news and a custom extension.",
  "license": "GPL-2.0+",
  "require": {
    "typo3/cms": "^7.6",
    "typo3-ter/news": "^4.0"
  "extra": {
    "typo3/cms": {
      "cms-package-dir": "{$vendor-dir}/typo3/cms",
      "web-dir": "web"
  "autoload": {
    "psr-4": {
      "DanielGoerz\\MyExtension\\": "web/typo3conf/ext/my_extension/Classes/"

So with this composer.json and after performing composer install, your project root should look something like this:

ls -l

The TYPO3 core is located in vendor/typo3/cms and is automatically symlinked by Composer to web/typo3. The index.php is also symlinked to web/index.php. If a new TER extension is added it must be added to the required section in the composer.json (composer require typo3-ter/realurl ^1.12). If a new extension is created in the project (and not downloaded by Composer) it's classes have to be registered in the autoload section of the composer.json.

The composer.json contains some general configuration for your TYPO3 project: The requirements, autoloading, web-folder, etc. The composer.lock file contains the exact version (e.g. a commit hash) of every package that was downloaded by Composer. The command composer install will only look in the composer.lock and installs the packages from the very versions stated there. This makes the installation process of Composer reliable. composer update will update the dependencies as well as the composer.lock. So, to update an installed TER extension (e.g. news) you have to run composer update typo3-ter/news. This will lead to changes in the composer.lock file (new commit for the news extension), which you have to commit to your git Repository.

But to be able to commit anything you have to put your project under version control first. So this is the next step.


Use Git

Using Git for version control should be a matter of course for every developer in every project. TYPO3 projects are no exception to that rule. You init your Git repository in the project root folder of your project. So the interesting part is what you want to have under version control and what you don't want included in your Git repository.

To pick up the Composer topic, you want to include the composer.json as well as the composer.lock file in your repository but you want to exclude the vendor folder, because it only contains 3rd party packages that are already under version control in their respective repositories. And because web/typo3 and web/index.php are created by Composer, you don't want those symlinks in your Git as well.

A quite minimal .gitignore file for a TYPO3 project could contain the following



A few remarks on this .gitignore:

The file .gitkeep is used to make Git respect the typo3temp folder despite ignoring it's whole content. Because Git does not recognize empty folders we put a file into typo3temp to keep the directory in the Git repository. You should also not include the contents of fileadmin and uploads in your Git repository. Either do the same little trick or put the actual folders outside your project root and only add symlinks to the folders in your Git repository.

In terms of extensions you want to exclude every extension that is installed by Composer. You can do this either by listing each of them in the .gitignore file or do it the other way around and exclude all extensions and explicitly include only those extensions that are not fetched by composer.


Remember that your Git log is part of your projects documentation. A good guideline to follow are the TYPO3 rules for commit messages as they apply for patches of the TYPO3 core (link). Always write meaningful commit messages.


Use a configuration switch mechanism

You will run into the situation that you want a different system configuration on your development machine than you want in production. Because in development you want to see errors right away, in production you want them logged and be notified. The list of differences between the configurations of the same project in different environments is quite long. To give another example: you most likely don't have the same database credentials on every system your project is running on.

The main configuration of TYPO3 is stored in the file web/typo3conf/LocalConfiguration.php. All changes made in the install tool under "All Configuration" go there. However after the LocalConfiguration.php the AdditionalConfiguration.php is loaded. And in this file you can overwrite the configuration if you need to. So the first possibility to introduce a configuration switch is to include specific configuration files in the AdditionalConfiguration.php based on the environment. This could be achieved e.g. by using the ApplicationContext:

$currentContext = \TYPO3\CMS\Core\Utility\GeneralUtility::getApplicationContext();
if ($currentContex->isDevelopment()) {
  if (file_exists('path/to/DevelopmentConfiguration.php')) {

As a second concept targeting this issue I'd like to point to the dotenv connector written by Helmut Hummel. The connector allows the configuration of a TYPO3 system based on a .env file that is usually excluded from versioning and contains the overrides to TYPO3s configuration. You can require the dotenv connector in your root composer.json and use it right away.

To get startet you can also have a look at Helmuts TYPO3 distribution that makes use of the dotenv configuration switch mechanism as well as of composer. From the .env-example file we see that he introduced a new syntax to distinguish TYPO3 specific configuration from the rest of the environmental configuration:

# Set arbitrary TYPO3_CONF_VARS values, following the convention: TYPO3__<section>[__<sub-section>]__property
# Credentials
## Site name

There may be more ways to realize a configuration switch based on the environment and the good practice is not to use a particular one but to recognize the need for a clean solution on this matter that fits the rest of the projects conditions.


Keep everything clean and maintainable

Here is a list of more good practices that I came up with:

  • Include as few extensions as possible, especially from TER. Although the TER is a great place to find a ready-to-use solution for many use cases, keep in mind that with every extension you add the time you need for maintaining and (more important) upgrading your project increases. Evaluate carefully if an extension is really needed. Also have a look on how often the extension receives updates and keep your TER extension up to date. Usually they follow semantic versioning, so with a version requirement like ^2.0 in your composer.json you should always be able to update that package without a breaking change (because it would be 3.0 if it's breaking).
  • Use the latest version of TYPO3 (at least the latest LTS). TYPO3 continuously receives security patches and bugfixes in it's supported releases. Get used to always update the core if a new minor release comes out. You will get very fast at it very soon.
composer update typo3/cms --with-dependencies
git commit -m'[TASK] Update TYPO3 core' composer.lock
git push
  • Use fluid_styled_content. It defines its markup in fluid templates instead of in TypoScript. Do I need to say more? You can easily create custom elements. See my article about it.
  • Keep your database clean. This means first and foremost: Do not write TypoScript into the template record in the backend (with the exception of the INCLUDE_TYPOSCRIPT lines, of course). You want all your TypoScript be written in files that are under version control. It is absolutely possible to only have one line in the setup and one line in the constants as you can see in the screenshot from this very website.
  • Write clean code. Don't forget all you have learned about code quality just because you are in a TCA configuration file. There is no reason to get crazy with indentions and line breaks just because it is a long realurl config. Always be precise, always use PSR-2 when creating a new PHP file. For every other file type configure your IDE to behave always the same. Have a look at the .editorconfig concept. There is a plugin for phpstorm. When writing new extensions check out my article about Good practices in TYPO3 extensions.
    And don't cheat:
use TYPO3\CMS\Core\Utility\GeneralUtility as t3lib_div;

I saw this on TYPO3 Slack needed to share it =).


  • Do not put project related files in the fileadmin folder or any subfolder. No templates, no CSS, no JavaScript, no TypoScript. Yes it is possible - but many things that are possible are not automatically smart or beneficial to do. You always want an extension holding these files. And you always want to include them with EXT:. You want them under version control and you don't want them to be listable, editable or even deletable in the filelist backend module.


Use an automated deployment process

You should think about how you are going to deploy changes in your project to a target server. Doing it by hand is one way. But there might be better ways. Maybe create a checklist and rethink what a deployment process should do in your project. The list could look like this:

  1. Checkout the commit that you want to deploy.
  2. composer install
  3. Build your frontend (if you have a building tool like gulp integrated in your project)
  4. Perform tests
  5. Maybe show a Maintainance page for the time of the deployment
  6. copy the whole project to the target server (maybe to a new folder, maybe rsync it directly in the target folder)
  7. Update the database on the target server
  8. Create new upload folders on the target server
  9. Clear the caches
  10. Remove the Maintainance page
  11. Write a log, call the boss or just tweet about the successful deployment

The point is not that these are the right steps for deploying every TYPO3 project. Depending on the project there could be way more steps or of course there could be less. The point is that these steps are the same for every deployment and should be automated in some way.

A great way to make TYPO3 smoothly deployable, is to enhance the maintainability via CLI. Please head over to my article on that topic "TYPO3 and CLI". The short version is: use the typo3_console package or the coreapi extension.

There are many tools for this task out there (see a short list below). I want to point to one from the TYPO3 world: TYPO3 Surf. Surf has once been a Flow package but was decoupled from the framework by Helmut Hummel and Wouter Wolters in 2016. TYPO3 Surf is now a standalone deployment tool for PHP projects. I won't go into more detail here, if you are interested have look at the Github repository or the blog post "Deploy TYPO3 CMS using TYPO3 Surf" by Markus Schwemmer. And there is also a channel in the TYPO3 Slack.

But let me mention some other great tools for automated deployment processes:

  • Capistrano. Capistrano is written in Ruby. There are already some extensions for deploying TYPO3 with Capistrano publicly available: capistrano-typo3 on Github and capistrano-typo3-cms also on Github.
  • Deployer. Deployer ist written in PHP. There is a receipt for deploying TYPO3 with Deployer in the projects Github repository (see recipe/typo3.php at Github)
  • Rocketeer. Rocketeer is written in PHP.


Further Information