Skip to content
Sarj Code Standards

require-explicit-service-contract

Require an explicit contract for services that invoke collaborators or perform HTTP operations.

Why

A one-operation service can orchestrate other services without declaring the contract that callers consume and implementations and fakes satisfy.

Fix

Declare a focused ABC or Protocol covering the service operations and make that contract explicit on the implementation; reuse an existing contract instead of adding a nominal duplicate.

Examples

Before — flagged One service operation still needs its contract
app/service.py
class OrchestratorService:
def __init__(self, worker: Worker) -> None:
self.worker = worker
def run(self) -> None:
self.worker.run()
After — preferred The service declares its operation
app/service.py
from abc import ABC, abstractmethod
class Runner(ABC):
@abstractmethod
def run(self) -> None: ...
class OrchestratorService(Runner):
def __init__(self, worker: Worker) -> None:
self.worker = worker
def run(self) -> None:
self.worker.run()
Before — flagged Dispatching injected work establishes a service boundary
app/batching.py
import asyncio
from collections.abc import Callable
class Batcher:
def __init__(self, operation: Callable[[], None]) -> None:
self.operation = operation
async def run(self) -> None:
await asyncio.get_running_loop().run_in_executor(None, self.operation)
After — preferred The dispatcher declares its public operation
app/batching.py
import asyncio
from collections.abc import Callable
from typing import Protocol
class Operations(Protocol):
async def run(self) -> None: ...
class Batcher(Operations):
def __init__(self, operation: Callable[[], None]) -> None:
self.operation = operation
async def run(self) -> None:
await asyncio.get_running_loop().run_in_executor(None, self.operation)