Explicit code to reduce cost of change

Explicit code to reduce cost of change
Photo by Alexander Mils / Unsplash

I came to realize that most problems (bugs, higher costs of change, wrong estimations) in the projects I'm involved with, where due to implicit logic in the business domain.

What do I mean with implicit logic in the business domain? Things like the following:

class ProjectController
{
    public function updateProject(
        Project $project,
        AccessService $accessService,
    ): JsonResponse {
        $user = $this->getAuthenticatedUser();
        
        $accessService->checkProjectAccess($user, $project);
        
        ...
    }
}

Within the access service there would be a nested validation that validates the project, if the user has direct access to the project, if the user has access through the company of the project, whether he has the correct permissions, ...

Once we had a bug in the access concept and it took us 30 minutes alone to understand what was validated. Not to mention how long it took to see which other processes where affected by a change or the bug.

Nowadays we split those into small explicit checks on the element level which might look like this:

class ProjectController
{
    public function updateProject(
        Project $project,
    ): JsonResponse {
        $user = $this->getAuthenticatedUser();
        
        $user->mustNotBeLocked();
        $user->mustHavePermissionToManageProjects();
        $user->mustBeInSameCompany($project->company);
        
        ...
    }
}

This way ...

  1. it's obvious what is validated
  2. it's very easy to add additional checks on a specific action
  3. there is no way that changing the validation for one action affects another one

The downsides are, that ...

  1. it's more code to write
  2. it's possible to forget a check on one action

Although I'd argue that most of the time through the abstraction you don't even know what has to be validated and therefore you might forget it too.

In short, you exchange one problem for another and exchange higher cost of introduction for lower cost of change.