Skip to content

Turbo Evaluation Report

1. Executive Summary

This report evaluates the adoption of Turborepo for the Kemma repository.

Current State: Kemma is a polyglot monorepo containing a React/TypeScript frontend (apps/web-client) and a Python/FastAPI backend (apps/server). Development tasks are currently managed via root npm scripts that manually orchestrate commands for both apps.

Recommendation: Adopt Turbo. It will unify the developer experience, provide caching (speeding up CI and local development), and simplify task orchestration. The overhead of integrating the Python backend is minimal and manageable.

2. What is Turborepo?

Turborepo is a high-performance build system optimized for JavaScript/TypeScript monorepos, but capable of orchestrating any command. It creates a dependency graph of your tasks (build, test, lint) and executes them efficiently.

Key Features:

  • Caching: Turbo fingerprints the inputs (source files, configs) for a task. If the inputs haven't changed, it restores the output from the cache (local or remote) instead of re-running the task.
  • Parallelism: It schedules tasks to run concurrently across available CPU cores, respecting the dependency graph.
  • Orchestration: It manages dependencies between tasks (e.g., ensuring build runs before deploy).

3. Fit for Kemma (Polyglot Monorepo)

Kemma consists of:

  • apps/web-client: TypeScript/React (Native support in Turbo via NPM workspaces).
  • apps/server: Python/FastAPI (Supported via script wrappers).

While Turbo is JS-centric, it works well for polyglot repos by wrapping non-JS tasks in package.json scripts. This allows Turbo to orchestrate Python tasks (managed by uv) alongside Frontend tasks in a single pipeline.

4. Pros & Cons

Pros

  1. Unified Workflow:
    • Current: Developers must run npm run dev:web in one terminal and manually navigate to apps/server to run uv run ... in another.
    • With Turbo: turbo dev runs both the frontend and backend in parallel in a single terminal, with interleaved or filtered logs.
  2. Caching:
    • CI Optimization: If a PR only touches apps/web-client, Turbo will skip running backend tests (test:server) and backend linting, significantly reducing CI time.
    • Local Speed: Re-running build or lint commands locally is instant if files haven't changed.
  3. Simplified Scripts:
    • The root package.json scripts can be cleaned up. Complex chained commands like cd apps/server && uv run ... are replaced by simple delegation to Turbo.
  4. Scalability:
    • Sets a strong foundation for adding more applications (e.g., a background worker or a shared library) without complicating the build scripts.

Cons / Challenges

  1. Python "Wrapper" Boilerplate:
    • Turbo relies on package.json to discover workspaces. The Python backend currently lacks one.
    • Solution: We must create a minimal apps/server/package.json that defines scripts (e.g., "test": "uv run pytest") to bridge Turbo and uv.
  2. Learning Curve:
    • The team will need to learn how to configure turbo.json (defining inputs/outputs for caching).
  3. Documentation Cleanup:
    • Note: The dev/ directory contains legacy documentation (development-guide.md) referencing moon and Rust. Adopting Turbo confirms a different path, so these stale docs should eventually be removed or updated to avoid confusion.

5. Implementation Strategy

To adopt Turbo, the following steps are required:

Step 1: Update Workspace Configuration

Modify the root package.json to include the server directory in workspaces.

json
"workspaces": [
  "apps/web-client",
  "apps/server"
]

Step 2: Create Server Wrapper

Create apps/server/package.json to delegate tasks to uv. This makes the server a valid NPM workspace that Turbo can manage.

json
{
  "name": "server",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "dev": "uv run uvicorn kemma.main:app --reload --host 0.0.0.0 --port 8000",
    "test": "uv run pytest",
    "lint": "uv run ruff check kemma tests",
    "typecheck": "uv run pyright",
    "format": "uv run ruff format kemma tests",
    "clean": "rm -rf .pytest_cache .ruff_cache .mypy_cache htmlcov .coverage"
  }
}

Step 3: Configure Turbo

Install turbo (npm install turbo --save-dev) and create a turbo.json file in the root to define the pipeline.

json
{
  "$schema": "https://turbo.build/schema.json",
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
    },
    "lint": {},
    "typecheck": {},
    "test": {},
    "format": {},
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

Step 4: Update Root Scripts

Replace the manual scripts in the root package.json with Turbo commands.

json
"scripts": {
  "dev": "turbo dev",
  "build": "turbo build",
  "test": "turbo test",
  "lint": "turbo lint",
  "format": "turbo format",
  "check": "turbo typecheck lint test"
}

6. Conclusion

Adopting Turbo is highly recommended. It effectively solves the orchestration challenges of running multiple parts of the stack and introduces caching that will scale with the project. The integration with the Python backend is straightforward via uv wrappers.

7. References