Skip to main content

Code Style

Beyond linting, SimSwarm has a handful of conventions that keep the codebase consistent and the runtime correct. These come from the project's architecture rules and are worth following in any contribution.

Linting

Backend code is linted with Ruff at line length 100 ([tool.ruff] in pyproject.toml). Run Ruff and both test suites before opening a PR — see Testing.

Feature co-location

Organize by feature, not by layer. Each feature owns its models, schemas, API routes, and business logic in one directory (saas/auth/, saas/jobs/, saas/gpu/, …). Keep files under roughly 300 lines; split into focused modules rather than growing one file. See Repository Structure.

App factory + dependency injection

The FastAPI app is built by a factory, saas.main:create_app(settings=None), which accepts an optional Settings so tests can inject their own configuration. Database access goes through the injectable saas.database:get_session dependency, which tests override with an in-memory SQLite session. Follow this pattern: depend on get_session rather than reaching for a global engine, so the code stays testable.

Sync DB writes from Celery/activities

When writing to the database from a Celery task or a Temporal activity, use synchronous psycopg2 — never the shared async SQLAlchemy pool. Mixing the async pool into worker/activity contexts causes InterfaceError and can wedge the event loop. The job persistence layer (saas/jobs/persistence*.py) exposes sync helpers for exactly this reason, and the workflow activities persist results at the source using them.

Cancel, not terminate, for Temporal

To stop a running workflow, use workflow cancel, never workflow terminate. Termination skips the workflow's finally block — and that finally is what tears the GPU pod down (fishcloud.terminate_pod in SimulationWorkflow). Skipping it leaves the pod running and billing. Cancel lets the cleanup path execute. See Data Flow and Temporal.

Engine via the adapter layer

The simulation engine lives in simswarm/. The application talks to it through saas/adapters/. Keep engine concerns out of the application feature directories and vice versa.

No credit gating

The open-source build has no billing and no credit gating. Job creation does not check or charge a balance — there is no 402 path. Some legacy billing artifacts remain on disk (an empty saas/billing/ directory, the dead credits_charged column, and the retained REFUNDED status), but they are not part of any active path. Do not reintroduce credit checks. See Database Schema.