Skip to Content
eventkit is in active development. APIs may change.
ContributingContributing

Contributing to eventkit

Thank you for your interest in contributing to eventkit! This guide will help you get started.

Code of Conduct

This project follows the Contributor Covenant Code of Conduct . By participating, you are expected to uphold this code.

Getting Started

Prerequisites

  • Python 3.11 or 3.12
  • Git
  • Google Cloud SDK (for Firestore emulator)

Fork and Clone

  1. Fork the repository on GitHub
  2. Clone your fork:
git clone https://github.com/YOUR_USERNAME/eventkit.git cd eventkit
  1. Add upstream remote:
git remote add upstream https://github.com/prosdev/eventkit.git

Install Dependencies

# Install in editable mode with dev dependencies pip install -e ".[dev]" # Set up pre-commit hooks (optional but recommended) pre-commit install

Start Firestore Emulator (for integration tests)

gcloud emulators firestore start --host-port=localhost:8080

In another terminal:

export FIRESTORE_EMULATOR_HOST=localhost:8080

Verify Setup

# Run tests pytest # Type check mypy src/eventkit # Lint ruff check src/

Development Workflow

We follow Spec-Driven Development (inspired by GitHub’s spec-kit ):

  1. Spec → Define what to build (specs/*/spec.md)
  2. Plan → Define how to build it (specs/*/plan.md)
  3. Tasks → Break into atomic tasks (specs/*/tasks.md)
  4. Implement → Build it, following TDD

See Spec-Driven Development for detailed process.

Working on a Feature

  1. Check existing specs in specs/ directory
  2. Pick a task from specs/*/tasks.md
  3. Create a branch:
git checkout -b feature/P1-T001-raw-event-model
  1. Write tests first (TDD):
# Create test file touch tests/unit/schema/test_raw.py # Write failing tests # Then implement to make them pass
  1. Implement the feature
  2. Run tests:
pytest tests/unit/schema/test_raw.py -v
  1. Check quality:
# Type check mypy src/eventkit # Lint ruff check src/ # Format ruff format src/
  1. Commit with conventional commit message (see below)
  2. Push and create PR

Project Structure

eventkit/ ├── src/ │ └── eventkit/ # Source code │ ├── schema/ # Data models (RawEvent, TypedEvent) │ ├── adapters/ # Event adapters and validators │ ├── processing/ # Sequencer, buffer, processor │ ├── stores/ # Storage interfaces and implementations │ ├── api/ # FastAPI routes │ ├── logging/ # Structured logging │ └── errors/ # Custom exceptions ├── tests/ │ ├── unit/ # Fast, isolated tests │ ├── integration/ # Multi-component tests │ └── performance/ # Throughput and latency tests ├── specs/ # Specifications and plans │ └── core-pipeline/ │ ├── spec.md # User stories │ ├── plan.md # Implementation plan │ └── tasks.md # Task breakdown ├── examples/ # Usage examples ├── CLAUDE.md # AI agent context ├── WORKFLOW.md # Spec-driven workflow ├── TESTING.md # Testing guide └── CONTRIBUTING.md # This file

Coding Standards

Python Style

  • PEP 8 compliant (enforced by ruff)
  • Type hints required (enforced by mypy in strict mode)
  • Docstrings for public APIs (Google style)
  • Maximum line length: 100 characters

Code Patterns

1. Use Protocols over Abstract Base Classes

Good:

from typing import Protocol class EventStore(Protocol): async def write(self, events: list[TypedEvent]) -> None: ...

Avoid:

from abc import ABC, abstractmethod class EventStore(ABC): @abstractmethod async def write(self, events: list[TypedEvent]) -> None: ...

2. Use Pydantic v2 Patterns

Good:

from pydantic import BaseModel, ConfigDict, Field class RawEvent(BaseModel): model_config = ConfigDict(extra="allow") payload: dict[str, Any]

Avoid:

class RawEvent(BaseModel): class Config: extra = "allow" # Pydantic v1 syntax

3. Never Reject at Edge

Good:

def adapt(self, raw: RawEvent) -> AdapterResult: if not self._is_valid(raw): return AdapterResult.err("Invalid event") # Return error return AdapterResult.ok(event)

Avoid:

def adapt(self, raw: RawEvent) -> TypedEvent: if not self._is_valid(raw): raise ValidationError("Invalid event") # Never raise in hot path

4. Async Throughout

Good:

async def enqueue(self, event: RawEvent) -> None: await self.processor.enqueue(event)

Avoid:

def enqueue(self, event: RawEvent) -> None: # Blocking I/O self.store.write(event)

5. Explicit is Better than Implicit

Good:

def get_partition_id(self, event: TypedEvent) -> int: """Route event to partition by identity hash. Uses FNV-1a hash for good distribution. Events with same userId always route to same partition for ordered processing. """ routing_key = self._get_routing_key(event) hash_value = self._fnv1a_hash(routing_key) return hash_value % self.num_partitions

Avoid:

def get_partition_id(self, event: TypedEvent) -> int: # Hash and modulo return hash(event.userId or event.anonymousId) % self.num_partitions

Testing Guidelines

See Testing Guidelines for comprehensive testing guide.

Quick Reference

  • Unit tests: Fast, isolated, mock dependencies
  • Integration tests: Multi-component, use Firestore emulator
  • Performance tests: Validate throughput/latency targets
  • Coverage target: >80%

Test-Driven Development (TDD)

Always write tests before implementation:

# 1. Write failing test # tests/unit/schema/test_raw.py def test_raw_event_accepts_arbitrary_fields(): event = RawEvent(payload={"custom": "field"}, stream="test") assert event.get("custom") == "field" # 2. Run test (should fail) pytest tests/unit/schema/test_raw.py::test_raw_event_accepts_arbitrary_fields # 3. Implement feature # src/eventkit/schema/raw.py class RawEvent(BaseModel): # ... implementation # 4. Run test (should pass) pytest tests/unit/schema/test_raw.py::test_raw_event_accepts_arbitrary_fields

Commit Message Guidelines

We use Conventional Commits  enforced by gitlint.

Format

<type>(<scope>): <subject> <body> <footer>

Types

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation changes
  • test: Adding or updating tests
  • refactor: Code refactoring (no behavior change)
  • perf: Performance improvements
  • chore: Build process, dependencies, tooling

Examples

Good:

feat(schema): add RawEvent model with flexible JSON support Implements Story 1 acceptance criteria: accept any JSON payload without rejection at edge. - ConfigDict(extra="allow") for arbitrary fields - get() helper method for safe field access - Unit tests with 100% coverage Closes #12
fix(buffer): prevent race condition in partition flush EventLoader._flush_partition was not thread-safe when called from both size-based and time-based triggers simultaneously. Added asyncio.Lock per partition to serialize flushes.
test(adapters): add missing test for unknown event type Story 3 acceptance criteria requires returning error for unknown event types. Added test case.

Avoid:

updated stuff
WIP
feat: added some changes to the code

Pre-commit Hook

Commit messages are validated automatically if you’ve installed pre-commit hooks:

pre-commit install --hook-type commit-msg

Pull Request Process

Before Submitting

  1. ✅ All tests pass (pytest)
  2. ✅ Type checking passes (mypy src/eventkit)
  3. ✅ Linting passes (ruff check src/)
  4. ✅ Code is formatted (ruff format src/)
  5. ✅ Coverage >80% for new code
  6. CHANGELOG.md updated (if applicable)
  7. ✅ Docstrings added/updated

PR Title

Use conventional commit format:

feat(api): add batch event support to /collect endpoint

PR Description Template

## Summary Brief description of what this PR does. ## User Story Which user story does this implement? (e.g., Story 1: Flexible Event Ingestion) ## Tasks Completed - [ ] P1-T001: Create schema/__init__.py - [ ] P1-T002: Implement RawEvent model - [ ] P1-T004: Write unit tests for RawEvent ## Changes - Added RawEvent model with ConfigDict(extra="allow") - Added get() helper method - Added 5 unit tests with 100% coverage ## Testing - Unit tests: `pytest tests/unit/schema/test_raw.py` - Coverage: 100% for new code - Manual testing: N/A (no API changes) ## Breaking Changes None ## Screenshots (if applicable) N/A ## Checklist - [x] Tests added/updated - [x] Documentation updated - [x] Type hints added - [x] Linting passes - [x] No breaking changes (or documented)

Review Process

  1. Automated checks run on every PR (GitHub Actions)
  2. Code review by maintainer
  3. Address feedback in new commits (don’t force-push during review)
  4. Squash and merge once approved

Code Review Guidelines

For Reviewers

  • Be respectful and constructive
  • Focus on code, not the person
  • Ask questions, don’t demand changes
  • Approve if code meets standards (doesn’t have to be perfect)
  • Block if: breaking changes, missing tests, security issues

For Contributors

  • Respond to all comments
  • Don’t take feedback personally
  • Ask for clarification if feedback is unclear
  • Update code based on feedback
  • Mark conversations as resolved when addressed

Documentation

When to Update Docs

  • README.md: New features, API changes, installation steps
  • CHANGELOG.md: All user-facing changes
  • specs/: New features requiring design discussion
  • TESTING.md: New testing patterns or tools
  • Docstrings: All public APIs

Docstring Format (Google Style)

def get_partition_id(self, event: TypedEvent) -> int: """Route event to partition by identity hash. Uses FNV-1a hash of userId or anonymousId to ensure consistent routing. Events for same user always go to same partition. Args: event: Typed event to route Returns: Partition ID (0 to num_partitions-1) Examples: >>> sequencer = Sequencer(num_partitions=16) >>> event = IdentifyEvent(userId="user_123", ...) >>> partition = sequencer.get_partition_id(event) >>> assert 0 <= partition < 16 """

Performance Considerations

Write Efficient Code

  • Use async/await for I/O operations
  • Batch operations when possible
  • Avoid N+1 queries
  • Profile before optimizing

Benchmark Changes

If your PR affects performance-critical paths:

pytest tests/performance/ --benchmark-only

Include benchmark results in PR description.

Release Process

(For maintainers)

  1. Update pyproject.toml version
  2. Update CHANGELOG.md
  3. Create git tag: git tag v0.1.0
  4. Push tag: git push --tags
  5. GitHub Actions will build and publish to PyPI

Questions and Support

Where to Ask

  • GitHub Issues: Bug reports, feature requests
  • GitHub Discussions: Questions, ideas, general discussion
  • Pull Requests: Code review, implementation questions

Issue Templates

When creating an issue, use the appropriate template:

  • Bug Report: Describe bug, steps to reproduce, expected vs actual behavior
  • Feature Request: Describe feature, use case, acceptance criteria
  • Question: Ask question with context

Recognition

Contributors are recognized in:

  • README.md Contributors section
  • Release notes for significant contributions
  • GitHub Contributors page

Thank you for contributing to eventkit! 🎉

Last updated on