Refactor

Rebooting the Contract: Why our Tests fail when our Documentation is lying

How we migrated from deprecated Doctrine Annotations to native PHP 8 Attributes to enforce a living, breathing API contract.

Rebooting the Contract: Why our Tests fail when our Documentation is lying

The Obsession with Truth

In the BOS Era, we have zero tolerance for “ghost documentation.” You know the type: that shiny Swagger UI that promises a string, but when you hit the endpoint, the API spits out an integer (or worse, a null) and your frontend crashes in flames.

Documentation that lies is worse than no documentation at all. It builds a false sense of security that eventually erodes trust between engineering teams.

At dammgo, we decided to reboot our contract.


The Mess: Dead Comments

For years, the industry standard for documenting PHP APIs was Doctrine Annotations. It looked something like this:

/**
 * @OA\Get(
 *     path="/v1/operations/merchants/{id}",
 *     @OA\Response(response="200", description="Merchant details")
 * )
 */
public function getMerchant(string $id) { ... }

The problem? To the PHP engine, those are just comments. They are invisible ink. If you make a typo inside that DocBlock, your code runs fine, your tests pass, but your documentation dies. We were carrying doctrine/annotations as a heavy, deprecated dependency just to parse strings that should have been code.


The Strategy: Native Sovereignty

With PHP 8, we finally got Attributes. They are not comments; they are first-class citizens of the language. Our strategy was simple: delete the “invisible ink” and replace it with Evidence.

We migrated every single endpoint in the erpbsg API to native Attributes:

#[OA\Get(
    path: '/v1/operations/merchants/{id}',
    responses: [
        new OA\Response(response: '200', description: 'Merchant details')
    ]
)]
public function getMerchant(string $id) { ... }

Now, the compiler knows. Reflection knows. And most importantly, our testing suite knows.


The Craft: The “Living Contract” Mandate

This is where the magic (and the discipline) happens. In our Testing_Guidelines.md, we established a mandate: No test shall pass if the documentation is lying.

Instead of using the standard request handler, every integration test must use our custom engine:

// Standard way (Don't do this)
$response = $this->app->handle($request);

// The BOS Way (Engineering as Art)
$response = $this->executeRequestAndValidate($request);

How it works:

  1. Intercept: The method handles the HTTP request.
  2. Compare: It captures the JSON response and compares it—field by field—against the openapi.json generated from our Attributes.
  3. Enforce: If you added a phone_number field to the response but forgot to update the #[OA\Property] Attribute, the test fails.

The documentation is no longer a “favor” the developer does for the team; it is an integral part of the code’s integrity.


The Result: Stability as an Asset

By rebooting our contract, we achieved three things:

  1. Zero Dependencies: We removed doctrine/annotations and doctrine/lexer.
  2. Total Truth: Our documentation (Atlas) is now a 100% accurate reflection of reality.
  3. Confidence: We can refactor with the certainty that if we break the contract, the “Anillos de Poder” (our test suite) will turn red instantly.

This isn’t just about clean code. It’s about Sovereignty. We own our infrastructure, we own our data, and now, we own our truth.


DAMMGO HOLDING - Engineering Longevity.