Skip to content

Latest commit

 

History

History
88 lines (62 loc) · 3.57 KB

File metadata and controls

88 lines (62 loc) · 3.57 KB

ADR-003: Configuration Generation Over Manual Configuration

Status: Accepted Date: 2025-02-28 Deciders: Walmir Silva Context: KaririCode Framework Devkit v1.0.0

Context

Each quality tool requires a configuration file (phpunit.xml.dist, phpstan.neon, php-cs-fixer.php, rector.php, psalm.xml). Across 35+ components, these files are near-identical, differing only in:

  • Source directory paths (derived from PSR-4 autoload)
  • Test suite directory structure
  • PHP version target
  • PHPStan analysis level

Maintaining 175+ config files manually leads to configuration drift, inconsistent rules, and merge-conflict friction when updating standards.

Decision

Config files are generated deterministically from a ProjectContext snapshot by ConfigGenerator implementations. The generation cycle is:

composer.json → ProjectDetector → ProjectContext → ConfigGenerator → .kcode/*.config

Manual customization is achieved through a single override file (devkit.php) that is merged with ecosystem defaults — not by editing generated configs.

Generated files include a header comment: Generated by KaririCode Devkit — override via devkit.php (project root).

Rationale

Generation Advantages

  1. Single source of truth — Coding standards and analysis rules live in ProjectDetector::DEFAULT_CS_RULES and DEFAULT_RECTOR_SETS. Updating the devkit updates all components.
  2. Deterministic output — Same ProjectContext always produces the same config files. No hidden state, no manual edits to preserve.
  3. Override granularitydevkit.php supports per-project customization at the key level (PHPStan level, extra CS rules, excluded directories) without duplicating the entire config.
  4. Safe regenerationkcode init can be run repeatedly. Generated files are disposable artifacts.

Override Merging Strategy

// devkit.php — only specify what differs from defaults
<?php return [
    'phpstan_level'    => 8,                        // override: lower level
    'exclude_dirs'     => ['src/Contract', 'src/Legacy'], // override: extra exclusion
    'cs_fixer_rules'   => ['yoda_style' => false],  // merge: added to defaults
];

The merge strategy varies by key type:

Key Strategy Rationale
Scalar (phpstan_level, php_version) Replace Project-specific requirement
List (source_dirs, exclude_dirs) Replace Full override for clarity
Map (cs_fixer_rules) array_merge Additive customization
Map (test_suites) Replace Suite structure is project-specific

Alternatives Considered

Alternative Rejected Because
Symlinks to shared configs Doesn't support per-project overrides
Composer scripts + templates Requires Twig/Blade; adds dependencies
.dist files with manual copy Config drift returns immediately
Central config repo + git submodule Poor developer experience; merge conflicts

Consequences

Positive

  • Zero config drift across the ecosystem.
  • One-command setup for new components: kcode init.
  • Override file is version-controlled alongside the project.
  • Generated configs are gitignored-friendly (optional).

Negative

  • Developers cannot hand-edit generated configs (edits are overwritten on next init).
  • The override merge logic must be well-documented to avoid confusion.
  • Adding new config keys requires a devkit release.

References

  • Microsoft documentation generators: config-as-code principle
  • Terraform HCL: override files merged with base configuration
  • ARFA 1.3 Specification, §4.3: Deterministic Configuration