Skip to content
Sarj Code Standards

no-service-behavior-in-settings

Settings and configuration types should not orchestrate injected collaborators.

Why

A class named as settings or configuration promises passive data. Calling injected stores, clients, or services from that object hides orchestration behind a data boundary and couples configuration construction to runtime behavior.

Fix

Move collaborator calls into a service, coordinator, or compiler with an honest behavioral name; keep the settings/configuration object as a TypedDict, Pydantic model, attrs class, or dataclass.

Examples

Before — flagged Native syntax preserves the existing ownership contract
app/settings.py
class Settings:
def __init__(self, store: ScheduleStore) -> None:
self._store = store
async def repoint(self, batch_id: str) -> int:
return await self._store.repoint(batch_id)
After — preferred Existing semantic ownership exclusions remain valid
app/settings.py
class Settings:
def __init__(self, size: int):
self.size = size
def double(self):
return self.size * 2
Before — flagged A settings-named class calls injected persistence
app/batch_settings.py
class BatchSettings:
def __init__(self, store: ScheduleStore) -> None:
self._store = store
async def repoint(self, batch_id: str) -> int:
return await self._store.repoint(batch_id)
After — preferred A settings type remains a passive record
app/batch_settings.py
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class BatchSettings:
retry_limit: int