Cohesion
Coupling
Cohesion - what classes belong to a package
Coupling - relations between packages
You can only reuse the amount of code that you can actually release
“Before software can be reusable it first has to be usable.” – Ralph Johnson
Classes that are used together are packaged together
{
    "require": {
        "php": "^7.3",
        "ext-dom": "*",
        "ext-json": "*",
        "ext-libxml": "*",
        "symfony/yaml": "^3.4.29 || ^4.0 || ^5.0",
        "symfony/console": "^3.4.29 || ^4.0 || ^5.0",
        "symfony/filesystem": "^3.4.29 || ^4.0 || ^5.0",
        "symfony/finder": "^3.4.29 || ^4.0 || ^5.0",
        "symfony/process": "^3.4.29 || ^4.0 || ^5.0",
        "composer/xdebug-handler": "^1.3.3",
        "justinrainbow/json-schema": "^5.2",
        "nikic/php-parser": "^4.2.2",
        "sanmai/pipeline": "^3.1",
        "sebastian/diff": "^3.0.2 || ^4.0",
        "seld/jsonlint": "^1.7"
    }
}
infection/core
{
    "require": {
        "php": "^7.3",
        "ext-dom": "*",
        "ext-json": "*",
        "ext-libxml": "*",
-       "symfony/yaml": "^3.4.29 || ^4.0 || ^5.0",
        "symfony/console": "^3.4.29 || ^4.0 || ^5.0",
        "symfony/filesystem": "^3.4.29 || ^4.0 || ^5.0",
        "symfony/finder": "^3.4.29 || ^4.0 || ^5.0",
        "symfony/process": "^3.4.29 || ^4.0 || ^5.0",
        "composer/xdebug-handler": "^1.3.3",
        "justinrainbow/json-schema": "^5.2",
        "nikic/php-parser": "^4.2.2",
        "sanmai/pipeline": "^3.1",
        "sebastian/diff": "^3.0.2 || ^4.0",
        "seld/jsonlint": "^1.7"
    }
}
infection/core
Violation inside Infection
Problem
Solution
# composer.json
{
    "name": "infection/mutation-badge",
    "require": {
        "ext-curl": "*"
    }
}# composer.json
{
    "name": "infection/codeception-adapter",
    "conflict": {
        "codeception/codeception": "<3.1.1"
    }
}Instead of real dependencies, only suggestions:
# composer.json
{
    "suggest": {
        "graylog2/gelf-php": "Allow sending to a GrayLog2 server",
        "sentry/sentry": "Allow sending to a Sentry server",
        "doctrine/couchdb": "Allow sending to a CouchDB server",
        "ruflin/elastica": "Allow sending to an Elastic Search server",
        "php-amqplib/php-amqplib": "Allow sending to an AMQP server",
        "ext-amqp": "Allow sending to an AMQP server (1.0+ required)",
        "ext-mongo": "Allow sending to a MongoDB server",
        "mongodb/mongodb": "Allow sending to a MongoDB",
        "aws/aws-sdk-php": "Allow sending to AWS services like DynamoDB",
        "rollbar/rollbar": "Allow sending to Rollbar",
        "php-console/php-console": "Allow sending to Google Chrome"
    }
}monolog/monolog
Conclusion
The classes in a package should be closed together against the same kinds of changes.
GET /api/products?featureId=234
[
    {"id":  1, "name":  "Product 1"},
    {"id":  2, "name":  "Product 2"},
    {"id":  3, "name":  "Product 3"},
    {"id":  4, "name":  "Product 4"}
]
// 4 elements (all included)Before (2.4.3)
After (2.4.7)
[]
// 0 elements (all excluded)SELECT .. FROM .. WHERE p.feature_id=0SELECT .. FROM ..GET /api/products?featureId=string
Also, Common Reuse Principle is violated
Conclusion
The dependency structure between packages must be a directed acyclic graph.
No cycles
With cycle
public function runTests(TestFrameworkAdapterInterface $adapter): void
{ 
    // ...
    
    if ($adapter->testsPass()) {
    	// ...
    }
}class PhpUnitAdapter implements TestFrameworkAdapterInterface {}
class PhpSpecAdapter implements TestFrameworkAdapterInterface {}
class CodeceptionAdapter implements TestFrameworkAdapterInterface {}Case with release a new major 2.0.0 version
interface TestFrameworkAdapterInterface
{
    public function testsPass(): bool;
+   public function getVersion(): string;
}# project's composer.json
{
    "require-dev": {
        "infection/core": "^1.0",
        "infection/phpunit-adapter": "^1.0"
    }
}
# project's composer.json
{
    "require-dev": {
        "infection/core": "^1.0"
    }
}
Stable, Irresponsible
Unstable
Responsible
Instability Value
Instability Value
Instability Value
Instability Value
Instability Value
# phpspec adapter's composer.json
{
    "require": {
        "infection/core": "^0.13 || ^0.14 ... || 1.0.0",
        "symfony/yaml": "^3.4.29 || ^4.0 || ^5.0",
    }
}Instability Value
# phpspec adapter's composer.json
{
    "require": {
-       "infection/core": "^0.13 || ^0.14 ... || 1.0.0",
+       "infection/abstract-testframework-adapter": "^1.0.0"
    }
}I=0
Instability Value
Abstractness Value
Abstractness Value
Cohesion - what classes belong to a package
Coupling - relations between packages
Before:
After:
# project's composer.json
{
    "require-dev": {
        "infection/core": "^1.0",
        "infection/codeception-adapter": "^1.0"
    }
}
# project's composer.json
{
    "require-dev": {
        "infection/core": "^1.0"
    }
}
{
    "name": "infection/codeception-adapter",
    "type": "infection-extension",
    "extra": {
        "infection": {
            "class": "Infection\\TestFramework\\Codeception\\CodeceptionAdapterFactory"
        }
    }
}Infection analyzes composer.lock file and autoregisters all Infection plugins.
Before:
After:
# project's composer.json
{
    "require-dev": {
        "infection/core": "^1.0",
        "infection/phpunit-adapter": "^1.0"
    }
}
# project's composer.json
{
    "require-dev": {
        "infection/core": "^1.0",
        "infection/phpspec-adapter": "^1.0"
    },
    "repositories": [
        {
            "type": "path", 
            "url": "../path-to-local/phpspec-adapter"
        }
    ]
}
# studio.json
{
    "version": 2,
    "paths": [
        "../infection-phpspec-adapter"
    ]
}Monolith
Multiple Repositories
Monorepo
# infection/core
composer.json
/src
    /Mutator
    /Differ
    ...
    
# infection/codeception-adapter
composer.json
/src
    CodeceptionAdapter.php
    Version.php
    ...
    
# infection/phpunit-adapter
composer.json
/src
    PhpunitAdapter.php
    Version.php# infection/infection
composer.json
/src
    ...
    /Differ
    /Mutator
    /TestFramework
        /Codeception
        /PhpSpec
        /PhpUnit# infection/infection
/packages
    /infection-core
        /src
        composer.json
    /infection-codeception-adapter
        /src
        composer.json
    /infection-phpspec-adapter
        /src
        composer.json
    ...?
# infection/infection
/packages
    /infection-core
        /src
        composer.json
    /infection-codeception-adapter
        /src
        composer.json
    /infection-phpspec-adapter
        /src
        composer.json
    ...