Skip to content

Contributing

Thank you for your interest in Subscribe Flow! Here you will learn how to contribute.

Code of Conduct

We expect all contributors to interact respectfully. Harassment and discriminatory behavior will not be tolerated.

How Can I Contribute?

Bug Reports

  1. Check whether the bug has already been reported
  2. Create an issue with:
  3. Description of the problem
  4. Steps to reproduce
  5. Expected vs. actual behavior
  6. Environment (OS, Python version, etc.)

Feature Requests

  1. Check existing feature requests
  2. Describe the use case
  3. Explain the expected benefit

Pull Requests

  1. Fork the repository
  2. Create a feature branch
  3. Implement your changes
  4. Write tests
  5. Create a pull request

Development Setup

Prerequisites

  • Python 3.13+
  • Docker & Docker Compose
  • uv (Package Manager)

Setup

Bash
1
2
3
4
5
6
7
8
9
# Clone the repository
git clone https://github.com/talent-factory/subscribe-flow.git
cd subscribe-flow

# Komplettes Setup
make setup

# Entwicklungsumgebung starten
make dev

Running Tests

Bash
1
2
3
4
5
6
7
8
# All tests
uv run pytest

# With coverage
uv run pytest --cov=subscribeflow

# Single test
uv run pytest tests/test_subscribers.py::test_create -v

Code Quality

Bash
1
2
3
4
5
6
7
8
# Linting
uv run ruff check .

# Formatting
uv run ruff format .

# Type checking
uv run mypy src/

Multi-Tenant Architecture

Alle Features müssen org-scoped sein

Jedes neue Feature muss die organization_id berücksichtigen. Daten müssen immer an eine Organisation gebunden und isoliert sein.

Richtlinien für neue Features

  • Alle Datenbank-Queries müssen nach organization_id filtern
  • API-Endpunkte erhalten die organization_id automatisch über den authentifizierten API-Key
  • Tests müssen mit mehreren Organisationen durchgeführt werden, um Tenant-Isolation zu verifizieren
  • Neue Models benötigen eine organization_id ForeignKey-Spalte

Beispiel: Org-scoped Query

Python
async def get_subscribers(
    db: AsyncSession,
    organization_id: UUID,
) -> list[Subscriber]:
    """Subscribers are always scoped to an organization."""
    stmt = (
        select(Subscriber)
        .where(Subscriber.organization_id == organization_id)
    )
    result = await db.execute(stmt)
    return list(result.scalars().all())

Beispiel: Multi-Org Test

Python
1
2
3
4
5
6
async def test_subscriber_isolation(db, org_a, org_b):
    """Subscribers from org_a must not be visible in org_b."""
    await create_subscriber(db, org_a.id, email="test@example.com")

    subscribers = await get_subscribers(db, org_b.id)
    assert len(subscribers) == 0

Stripe Billing Development

Für die Entwicklung von Billing-Features wird der Stripe Test Mode verwendet:

Bash
1
2
3
# .env
STRIPE_SECRET_KEY=sk_test_xxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxx
  • Verwende Stripe CLI für lokale Webhook-Tests: stripe listen --forward-to localhost:8000/webhooks/stripe
  • Teste mit Stripe Test Cards
  • Niemals Produktions-Keys in der lokalen Entwicklung verwenden

Coding Standards

Python Style

  • Use Python 3.13+ features
  • Type hints for all functions
  • Docstrings for public APIs
  • Line length: 100 characters
  • Ruff for linting and formatting

Example

Python
from typing import Annotated

from pydantic import BaseModel, EmailStr, Field


class SubscriberCreate(BaseModel):
    """Schema for subscriber creation."""

    email: Annotated[
        EmailStr,
        Field(description="Email address of the subscriber")
    ]
    first_name: Annotated[
        str | None,
        Field(default=None, max_length=100)
    ] = None


async def create_subscriber(
    db: AsyncSession,
    data: SubscriberCreate,
) -> Subscriber:
    """Creates a new subscriber.

    Args:
        db: Database session
        data: Subscriber data

    Returns:
        Created subscriber

    Raises:
        DuplicateError: Email already exists
    """
    # Implementation

Commit Messages

We use Conventional Commits with emojis:

Text Only
1
2
3
4
5
<emoji> <type>(<scope>): <description>

[optional body]

[optional footer]

Types:

Emoji Type Description
feat New feature
🐛 fix Bug fix
📚 docs Documentation
♻️ refactor Refactoring
🧪 test Tests
🔧 chore Maintenance

Examples:

Text Only
1
2
3
4
5
✨ feat(subscribers): add bulk import endpoint

🐛 fix(auth): handle expired tokens correctly

📚 docs: update API reference

Branch Naming

Text Only
1
2
3
4
5
<type>/<issue-id>-<description>

feature/123-bulk-import
bugfix/456-auth-error
docs/789-api-reference

Pull Request Process

Before the PR

  • Tests written and passing
  • Linting without errors
  • Documentation updated
  • Multi-Org Isolation getestet
  • Changelog entry (if relevant)

Documentation Verification

Bash
# Dokumentation bauen und auf Fehler prüfen
make docs-build

PR Template

Markdown
## Description

What was changed and why?

## Changes

- Change 1
- Change 2

## Testing

How were the changes tested?

## Checklist

- [ ] Tests added
- [ ] Multi-Org Isolation verified
- [ ] Documentation updated
- [ ] Linting passed

Review Process

  1. Automatic checks must pass
  2. At least one approval required
  3. No open conversations
  4. Merge via "Squash and Merge"

Documentation

MkDocs Locally

Bash
# Dependencies
uv sync --extra docs

# Start server
make docs

# Build (Verifikation)
make docs-build

# Manuell
uv run mkdocs serve
uv run mkdocs build

Docs Structure

Text Only
1
2
3
4
5
6
7
docs/
├── index.md              # Landing page
├── getting-started/      # Getting started
├── guides/               # How-to guides
├── architecture/         # Technical docs
├── reference/            # API reference
└── development/          # Contributor docs

Questions?

Thank you for your contribution!