diff --git a/BaseCommand.php b/BaseCommand.php
index dfd7f7b2db1bfd15fbfcb485b8585f21ba76ee1a..dde0fe93ec2efd0ebd455700560542be7b7dd21e 100644
--- a/BaseCommand.php
+++ b/BaseCommand.php
@@ -23,10 +23,11 @@ abstract class BaseCommand extends ComposerBaseCommand implements BaseCommandInt
   /**
    * {@inheritdoc}
    */
-  protected function execute(InputInterface $input, OutputInterface $output) {
+  protected function execute(InputInterface $input, OutputInterface $output): int {
     $class = $this->getHandlerClass();
     $this->handler = new $class($this->getComposer(), $this->getIO());
     $this->handler->setInput($input);
+    return 0;
   }
 
 }
diff --git a/BaseCommandInterface.php b/BaseCommandInterface.php
index 62ffdde0f60cb003f315436a8e6943d3f59930a4..c825cc1e7839748d12eb1dda8a0dfee7201ccb79 100644
--- a/BaseCommandInterface.php
+++ b/BaseCommandInterface.php
@@ -12,6 +12,6 @@ interface BaseCommandInterface {
   /**
    * @return \LakeDrops\Component\Composer\BaseHandlerInterface
    */
-  public function getHandlerClass();
+  public function getHandlerClass(): BaseHandlerInterface;
 
 }
diff --git a/BaseHandler.php b/BaseHandler.php
index 0d41d745c564d8c0919952c7ee915f3da2de80b4..504c388eb6d553e250f3b6dc4d79e553bc177c55 100644
--- a/BaseHandler.php
+++ b/BaseHandler.php
@@ -4,6 +4,7 @@ namespace LakeDrops\Component\Composer;
 
 use Composer\Composer;
 use Composer\IO\IOInterface;
+use Composer\Package\PackageInterface;
 use Composer\Script\Event;
 use Symfony\Component\Console\Input\InputInterface;
 
@@ -28,6 +29,13 @@ abstract class BaseHandler implements BaseHandlerInterface {
    */
   protected $io;
 
+  /**
+   * The project config.
+   *
+   * @var \LakeDrops\Component\Composer\Config
+   */
+  protected $config;
+
   /**
    * @var \Symfony\Component\Console\Input\InputInterface
    */
@@ -58,12 +66,20 @@ abstract class BaseHandler implements BaseHandlerInterface {
   public function __construct(Composer $composer, IOInterface $io) {
     $this->composer = $composer;
     $this->io = $io;
+    $this->config = new Config($this->configId(), $this->configDefault());
+  }
+
+  /**
+   * @return array
+   */
+  protected function configDefault(): array {
+    return [];
   }
 
   /**
    * {@inheritdoc}
    */
-  public function setEvent(Event $event) {
+  public function setEvent(Event $event): BaseHandler {
     $this->event = $event;
     return $this;
   }
@@ -71,7 +87,7 @@ abstract class BaseHandler implements BaseHandlerInterface {
   /**
    * {@inheritdoc}
    */
-  public function setInput(InputInterface $input) {
+  public function setInput(InputInterface $input): BaseHandler {
     $this->consoleInput = $input;
     return $this;
   }
@@ -79,7 +95,7 @@ abstract class BaseHandler implements BaseHandlerInterface {
   /**
    * {@inheritdoc}
    */
-  public function getDrupalCorePackage() {
+  public function getDrupalCorePackage(): PackageInterface {
     if (!isset($this->drupalCorePackage)) {
       $this->drupalCorePackage = $this->getPackage('drupal/core');
     }
@@ -89,7 +105,7 @@ abstract class BaseHandler implements BaseHandlerInterface {
   /**
    * {@inheritdoc}
    */
-  public function getPackage($name) {
+  public function getPackage(string $name): PackageInterface {
     return $this->composer->getRepositoryManager()->getLocalRepository()->findPackage($name, '*');
   }
 
@@ -125,21 +141,21 @@ abstract class BaseHandler implements BaseHandlerInterface {
   /**
    * {@inheritdoc}
    */
-  public function git($command) {
+  public function git(string $command): void {
     Utils::git($command);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function gitIgnore($pattern) {
+  public function gitIgnore(string $pattern): void {
     Utils::gitIgnore($pattern);
   }
 
   /**
    * {@inheritdoc}
    */
-  public function gitLFS($pattern) {
+  public function gitLFS(string $pattern): void {
     Utils::gitLFS($pattern);
   }
 
diff --git a/BaseHandlerInterface.php b/BaseHandlerInterface.php
index e818168b86c7023340630afd2da3a9f4712bfa06..330bb2985d06182a92c6f80fc59ccbe8d80046ae 100644
--- a/BaseHandlerInterface.php
+++ b/BaseHandlerInterface.php
@@ -2,6 +2,7 @@
 
 namespace LakeDrops\Component\Composer;
 
+use Composer\Package\PackageInterface;
 use Composer\Script\Event;
 use Symfony\Component\Console\Input\InputInterface;
 
@@ -12,6 +13,11 @@ use Symfony\Component\Console\Input\InputInterface;
  */
 interface BaseHandlerInterface {
 
+  /**
+   * @return string
+   */
+  public function configId(): string;
+
   /**
    * @param \Composer\Script\Event $event
    */
@@ -28,7 +34,7 @@ interface BaseHandlerInterface {
    * @return \Composer\Package\PackageInterface
    *   The Drupal core package.
    */
-  public function getDrupalCorePackage();
+  public function getDrupalCorePackage(): PackageInterface;
 
   /**
    * Retrieve a package from the current composer process.
@@ -39,24 +45,24 @@ interface BaseHandlerInterface {
    * @return \Composer\Package\PackageInterface
    *   The package.
    */
-  public function getPackage($name);
+  public function getPackage(string $name): PackageInterface;
 
   /**
    * @return bool
    */
-  public function isDevMode();
+  public function isDevMode(): bool;
 
   /**
    * @return bool
    */
-  public function isLocalDevMode();
+  public function isLocalDevMode(): bool;
 
   /**
    * Determine if the current process runs in a CI/CD context.
    *
    * @return bool
    */
-  public function isCiContext();
+  public function isCiContext(): bool;
 
   /**
    * Wrapper for git command in the root directory.
@@ -64,7 +70,7 @@ interface BaseHandlerInterface {
    * @param string $command
    *   Git command name, arguments and/or options.
    */
-  public function git($command);
+  public function git(string $command);
 
   /**
    * Add the given pattern to gitignore if necessary.
@@ -72,7 +78,7 @@ interface BaseHandlerInterface {
    * @param string $pattern
    *   The pattern which should be ignored.
    */
-  public function gitIgnore($pattern);
+  public function gitIgnore(string $pattern);
 
   /**
    * Add the given pattern to git lfs if necessary.
@@ -80,6 +86,6 @@ interface BaseHandlerInterface {
    * @param string $pattern
    *   The pattern which should be added.
    */
-  public function gitLFS($pattern);
+  public function gitLFS(string $pattern);
 
 }
diff --git a/BasePlugin.php b/BasePlugin.php
index ee27ba80cb4bd886912da41090e249eefebdd797..d0faaf67a5a2f862a64ac6b96f2a7797dcd7ec93 100644
--- a/BasePlugin.php
+++ b/BasePlugin.php
@@ -23,7 +23,7 @@ abstract class BasePlugin implements BasePluginInterface, PluginInterface, Event
   /**
    * {@inheritdoc}
    */
-  public function activate(Composer $composer, IOInterface $io) {
+  public function activate(Composer $composer, IOInterface $io): void {
     $class = $this->getHandlerClass();
     $this->handler = new $class($composer, $io);
   }
@@ -31,11 +31,11 @@ abstract class BasePlugin implements BasePluginInterface, PluginInterface, Event
   /**
    * {@inheritdoc}
    */
-  public function deactivate(Composer $composer, IOInterface $io) {}
+  public function deactivate(Composer $composer, IOInterface $io): void {}
 
   /**
    * {@inheritdoc}
    */
-  public function uninstall(Composer $composer, IOInterface $io) {}
+  public function uninstall(Composer $composer, IOInterface $io): void {}
 
 }
diff --git a/BasePluginInterface.php b/BasePluginInterface.php
index 903eb902a341b6f233d4862846e7fbd69a10c7a2..cc11cb524f4b9d8302ca4827a61c8e276ec653c3 100644
--- a/BasePluginInterface.php
+++ b/BasePluginInterface.php
@@ -12,6 +12,6 @@ interface BasePluginInterface {
   /**
    * @return \LakeDrops\Component\Composer\BaseHandlerInterface
    */
-  public function getHandlerClass();
+  public function getHandlerClass(): BaseHandlerInterface;
 
 }
diff --git a/Config.php b/Config.php
new file mode 100644
index 0000000000000000000000000000000000000000..c153c6fb54fdd2bd466c23008a84021b30b3334e
--- /dev/null
+++ b/Config.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace LakeDrops\Component\Composer;
+
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * Manages project config.
+ */
+final class Config {
+
+  private static $values;
+
+  /**
+   * Config constructor.
+   *
+   * @param string $component
+   * @param array $default_values
+   */
+  public function __construct(string $component, array $default_values) {
+    $this->init();
+    $componentValues = self::$values[$component] ?? [];
+    self::$values[$component] = NestedArray::mergeDeep($default_values, $componentValues);
+  }
+
+  /**
+   * Read project configuration if not already happened.
+   */
+  private function init(): void {
+    if (self::$values === NULL) {
+      $configFile = getcwd() . '/.project.yaml';
+      if (!file_exists($configFile)) {
+        file_put_contents($configFile, Yaml::dump([], 9, 2));
+      }
+      self::$values = Yaml::parseFile($configFile);
+    }
+  }
+
+}
diff --git a/NestedArray.php b/NestedArray.php
new file mode 100644
index 0000000000000000000000000000000000000000..fc3699d19bc3cca613ea2c08f6699a92658ca138
--- /dev/null
+++ b/NestedArray.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace LakeDrops\Component\Composer;
+
+/**
+ * Class NestedArray.
+ *
+ * @package LakeDrops\Docker4Drupal
+ */
+class NestedArray {
+
+  /**
+   * Deeply merges arrays. Borrowed from drupal.org/project/core.
+   *
+   * @return array
+   *   The merged array.
+   */
+  public static function mergeDeep(): array {
+    return self::mergeDeepArray(func_get_args());
+  }
+
+  /**
+   * Deeply merges arrays. Borrowed from drupal.org/project/core.
+   *
+   * @param array $arrays
+   *   An array of array that will be merged.
+   * @param bool $preserve_integer_keys
+   *   Whether to preserve integer keys.
+   *
+   * @return array
+   *   The merged array.
+   */
+  public static function mergeDeepArray(array $arrays, $preserve_integer_keys = FALSE): array {
+    $result = [];
+    foreach ($arrays as $array) {
+      foreach ($array as $key => $value) {
+        if (is_int($key) && !$preserve_integer_keys) {
+          $result[] = $value;
+        }
+        elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
+          $result[$key] = self::mergeDeepArray([$result[$key], $value], $preserve_integer_keys);
+        }
+        else {
+          $result[$key] = $value;
+        }
+      }
+    }
+    return $result;
+  }
+
+}
diff --git a/Utils.php b/Utils.php
index 89c48f9e56e786a5497fd406f479dfb819a1c8e2..9ff29bcaf2f7540fa3bc04fb3b2efd2f52c0f677 100644
--- a/Utils.php
+++ b/Utils.php
@@ -1,44 +1,12 @@
-<?php /** @noinspection UnusedConstructorDependenciesInspection */
+<?php
 
 namespace LakeDrops\Component\Composer;
 
-use Composer\Composer;
-use Composer\Json\JsonFile;
-use Exception;
-
 /**
  * Manages composer.json files.
  */
 final class Utils {
 
-  /**
-   * The composer object running this session.
-   *
-   * @var \Composer\Composer
-   */
-  private $composer;
-
-  /**
-   * Content of the composer.json file.
-   *
-   * @var array
-   */
-  private $content;
-
-  /**
-   * The path of the directory of the composer.json file.
-   *
-   * @var string
-   */
-  private $sourcePath;
-
-  /**
-   * The full filename of the composer.json file.
-   *
-   * @var string
-   */
-  private $composerJsonPath;
-
   /**
    * @var string[]
    */
@@ -49,141 +17,10 @@ final class Utils {
    */
   private static $lfsGitPatterns;
 
-  /**
-   * Utils constructor.
-   *
-   * @param \Composer\Composer $composer
-   *   The composer object running this session.
-   * @param string|null $sourcePath
-   *   The path name if not the project's root directory.
-   */
-  public function __construct(Composer $composer, $sourcePath = NULL) {
-    $this->composer = $composer;
-    $this->sourcePath = $sourcePath ?? getcwd() . $composer->getPackage()->getTargetDir();
-    $this->composerJsonPath = $this->sourcePath . '/composer.json';
-    $this->read();
-  }
-
-  /**
-   * Read the current content of the project's composer.json file.
-   *
-   * @return $this
-   */
-  public function read(): self {
-    $this->content = [];
-    if (file_exists($this->composerJsonPath)) {
-      $jsonFile = new JsonFile($this->composerJsonPath);
-      $this->content = $jsonFile->read();
-    }
-    return $this;
-  }
-
-  /**
-   * Write the config to composer.json file.
-   *
-   * @return $this
-   */
-  public function write(): self {
-    $jsonFile = new JsonFile($this->composerJsonPath);
-    if (file_exists($this->composerJsonPath)) {
-      $content = $jsonFile->read();
-      if (md5(json_encode($content)) === md5(json_encode($this->content))) {
-        // Nothing has changed.
-        return $this;
-      }
-    }
-    try {
-      $jsonFile->write($this->content);
-    }
-    catch (Exception $ex) {
-      // Let's ignore this, if composer.json is read-only there is a general problem.
-    }
-    return $this;
-  }
-
-  /**
-   * Get settings for [$key].
-   *
-   * @param string $key
-   *   The key for the section.
-   *
-   * @return array|string
-   *   The settings of the key.
-   */
-  public function getSection($key) {
-    return $this->content[$key] ?? [];
-  }
-
-  /**
-   * Get settings for [$key1][$key2].
-   *
-   * @param string $key1
-   *   The key for the section.
-   * @param string $key2
-   *   The key for the sub-section.
-   *
-   * @return array|string
-   *   The settings of the key.
-   */
-  public function getSubSection($key1, $key2) {
-    $section = $this->getSection($key1);
-    return $section[$key2] ?? [];
-  }
-
-  /**
-   * Get settings for [$key1][$key2][$key3].
-   *
-   * @param string $key1
-   *   The key for the section.
-   * @param string $key2
-   *   The key for the sub-section.
-   * @param string $key3
-   *   The key for the sub-sub-section.
-   *
-   * @return array|string
-   *   The settings of the key.
-   */
-  public function getSubSubSection($key1, $key2, $key3) {
-    $subSection = $this->getSubSection($key1, $key2);
-    return $subSection[$key3] ?? [];
-  }
-
-  /**
-   * Set settings for [$key].
-   *
-   * @param string $key
-   *   The key for the section.
-   * @param array|string $value
-   *   The value for the section.
-   *
-   * @return $this
-   */
-  public function setSection($key, $value): self {
-    $this->content[$key] = $value;
-    return $this;
-  }
-
-  /**
-   * Set settings for [$key1][$key2].
-   *
-   * @param string $key1
-   *   The key for the section.
-   * @param string $key2
-   *   The key for the sub-section.
-   * @param array|string $value
-   *   The value for the sub-section.
-   *
-   * @return $this
-   */
-  public function setSubSection($key1, $key2, $value): self {
-    $this->content[$key1][$key2] = $value;
-    return $this;
-  }
-
   /**
    * Make sure that git init was called.
    */
-  protected static function gitInit() {
+  protected static function gitInit(): void {
     if (!file_exists('.git')) {
       passthru(escapeshellcmd('git init'));
     }
@@ -192,7 +29,7 @@ final class Utils {
   /**
    * @param string $command
    */
-  public static function git($command) {
+  public static function git(string $command): void {
     self::gitInit();
     passthru(escapeshellcmd('git -c "user.email=composer@lakedrops.com" ' . $command));
   }
@@ -200,7 +37,7 @@ final class Utils {
   /**
    * @param string $pattern
    */
-  public static function gitIgnore($pattern) {
+  public static function gitIgnore(string $pattern): void {
     if (self::$ignoredGitPatterns === NULL) {
       if (file_exists('.gitignore')) {
         self::$ignoredGitPatterns = explode(PHP_EOL, file_get_contents('.gitignore'));
@@ -221,7 +58,7 @@ final class Utils {
   /**
    * @param string $pattern
    */
-  public static function gitLFS($pattern) {
+  public static function gitLFS(string $pattern): void {
     if (self::$lfsGitPatterns === NULL) {
       self::gitInit();
       $output = [];
diff --git a/composer.json b/composer.json
index 679d45f7501d47005519700c180448e618dce522..4ad938d1218e72c8bbedc00f54ad5153f566d139 100644
--- a/composer.json
+++ b/composer.json
@@ -25,7 +25,8 @@
     },
     "require": {
         "php": ">=7.2",
-        "ext-json": "*"
+        "ext-json": "*",
+        "symfony/yaml": "^3.4||^4.4||^5.0"
     },
     "require-dev": {
         "composer/composer": "^1||^2",