Skip to main content

Overview

MCC prioritizes maintainability over novelty. We prefer the smallest change that keeps the codebase coherent and boring. This guide will help you contribute effectively to the project.

Before You Start

Core Principles

When contributing to MCC, remember these five essential practices:
  1. Read the entry point and neighbors before changing anything
  2. State intent + boundaries (what you will change, what you will not)
  3. Keep diffs small and reviewable (≤5 files / ≤200 LOC unless requested)
  4. Follow existing repo patterns and architectural invariants
  5. Prove it works (run the relevant checks, or give exact commands + expected outputs)

Required Expertise

Contributors should be familiar with:
  • Rust (crates, cargo, traits, Salsa)
  • Compilers and language implementation (parsing, IR, codegen)
  • Tree-sitter and incremental compilation
  • C language and system tooling (preprocessor, assembler, linker)

Development Workflow

1. Read First

Before making any changes:
  • Find the exact entry point(s) involved:
    • CLI in mcc-driver
    • Pipeline stage in mcc
    • AST in mcc-syntax
    • Build tooling in xtask
  • Read neighboring code to learn local invariants and patterns
  • Search the codebase for existing conventions rather than guessing

2. State Intent and Boundaries

  • Briefly state what you’re changing, what you’re not changing, and why
  • If requirements are ambiguous, choose the safest minimal interpretation and state assumptions
  • Only ask questions if ambiguity would change public APIs, pipeline contracts, or crate boundaries

3. Implement in Small Steps

  • Keep changes logically grouped
  • Don’t mix refactors/formatting with functional changes
  • Keep changes locally scoped to the relevant crate/module

4. Prove It Works

  • Prefer test-driven / iterative verification (red → green → refactor)
  • Run the relevant verification commands
  • If you cannot run commands, list exactly what should be run and what success looks like

Verification Commands

Run the smallest relevant set for your change. CI uses these commands:

Full Workspace

cargo check --workspace --locked
cargo build --workspace --locked
cargo nextest run --workspace --locked
cargo test --doc --workspace --locked
cargo fmt --all -- --check
cargo clippy --workspace

Single Crate

cargo check -p <crate-name> --locked
cargo nextest run -p <crate-name> --locked

Integration Tests

From the repo root:
cargo test -p integration-tests --test integration
This runs the compiler against the writing-a-c-compiler-tests suite. Use --ignored to run tests beyond the default chapter cap.
If a command fails, fix it properly. Don’t skip checks to make things “look green”.

Hard Boundaries

Unless explicitly requested, do not:
  • Add new dependencies, crates, or workspace members
  • Introduce new pipeline stages or change stage boundaries
  • Put compilation logic in mcc-syntax or orchestration logic in mcc
  • Change Salsa tracking or break incremental compilation invariants
  • Restructure the pipeline (preprocessing → parsing → lowering → codegen → render → assembling)
If you believe a boundary must be crossed, explain:
  • The minimum change required
  • Why it’s necessary for the task
  • What risks it introduces

Maintainability Guardrails

No Bloat

Don’t add wrappers/adapters/helpers “for cleanliness” unless there is a concrete, local benefit.

Avoid Duplication

  • Don’t paste near-identical blocks across files
  • Prefer small helpers in the same module before creating new crates or shared libs
  • If duplication is unavoidable, explain why reuse isn’t appropriate

Interfaces and Traits

Introduce a trait only if:
  • There are at least two plausible implementations now, or
  • Tests clearly benefit and call sites depend on the trait (not the concrete type)

Don’t Paper Over Failures

  • Fix root causes; don’t “make tests pass” by weakening them
  • Avoid #[allow(...)] / ignoring lints unless you can justify why it’s safe

Security and Safety

  • Never commit secrets, tokens, API keys, private certs, or real credentials
  • Prefer .env/secret managers and clearly document required variables
  • Don’t log secrets (including in debug logs)

Generated Code

  • Don’t hand-edit generated files (e.g. tree-sitter grammar outputs) unless a local README explicitly allows it
  • Change the source of truth, then regenerate

Done Checklist

Before considering work “done”:
  • Diff is small and tightly scoped
  • No unnecessary abstractions/wrappers/scaffolding
  • No duplicated logic pasted across files
  • Follows repo conventions and architectural invariants
  • Relevant tests/checks pass (or you listed exact commands + expected results)
  • Changed contracts (public APIs, pipeline I/O, CLI flags, codegen inputs) are updated and documented

License

Contributions are dual-licensed under MIT OR Apache-2.0. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.

Build docs developers (and LLMs) love