Multi-Language Strategy¶
APX provides a symmetric developer experience across languages. The core principle: schemas are the published artifact, not generated stubs. Code is always generated locally using each language's native toolchain and packaging conventions.
Design Principles¶
-
Schema-first publishing -- APX releases schema files (
.proto, OpenAPI specs, Avro schemas) to a canonical repository or artifact registry. Generated code is never published. -
Local generation -- Every consumer generates code locally. This gives teams control over toolchain versions (protoc, grpc, etc.) and avoids version coupling between producer and consumer.
-
Same identity everywhere -- The same API identity (
proto/payments/ledger/v1) deterministically derives coordinates for every language. Dev and prod use the same identity; only the resolution backend changes. -
Native packaging -- Each language uses its own ecosystem's conventions. Go developers see Go modules, Python developers see pip packages, Java developers see Maven coordinates.
Language Support Matrix¶
| Language | Artifact | Local Overlay | Resolution | Codegen | Dev Command | Unlink Hint | Tier |
|---|---|---|---|---|---|---|---|
| Go | Go module | go.work use | go.work -> go.mod | apx gen go | apx sync | go get ... | Tier 1 |
| Cpp | Conan package | conan editable | Conan dependency | protoc + protoc-gen-grpc | conan install | Update conanfile | Tier 2 |
| Java | Maven JAR | mvn install | Maven dependency | protobuf-maven-plugin | mvn generate-sources | Update pom.xml | Tier 2 |
| Python | Python wheel | pip install -e | pkgutil namespace | apx gen python | apx link python | pip install ... | Tier 2 |
| Rust | Cargo crate | cargo path dep | Cargo dependency | tonic-build / prost-build | cargo build | Update Cargo.toml | Tier 2 |
| Typescript | npm package | npm link | npm/yarn | protoc + ts-proto | npm link | npm install ... | Tier 2 |
Tier definitions¶
- Tier 1 -- Full overlay lifecycle: generate, sync, link, unlink, release. First-class CI support.
- Tier 2 -- Identity derivation, generation, and local development. Release and CI support in progress.
- Planned -- Identity derivation designed but not yet implemented.
Identity Derivation by Language¶
Given org=acme and API path proto/payments/ledger/v1:
| Coordinate | Derived Value |
|---|---|
| Go module | github.com/acme/apis/proto/payments/ledger |
| Go import | github.com/acme/apis/proto/payments/ledger/v1 |
| Conan | acme-payments-ledger-v1-proto |
| C++ ns | acme::payments::ledger::v1 |
| Maven | com.acme.apis:payments-ledger-v1-proto |
| Java pkg | com.acme.apis.payments.ledger.v1 |
| Py dist | acme-payments-ledger-v1 |
| Py import | acme_apis.payments.ledger.v1 |
| Crate | acme-payments-ledger-v1-proto |
| Rust mod | acme_payments::ledger::v1 |
| npm | @acme/payments-ledger-v1-proto |
Organization Name Normalization¶
The org value from apx.yaml flows into every derived coordinate. Because each language ecosystem has its own identifier rules, APX normalizes the org name per-language:
| Context | Rule | Acme-Corp becomes |
|---|---|---|
| Package manager names (PyPI dist, Cargo crate, Conan ref, npm scope) | Lowercase only — hyphens are valid | acme-corp |
| OCI / Docker image refs | Must be fully lowercase | acme-corp |
| Go module paths | Case-insensitive (Git hosting) | acme-corp |
| Python imports | Hyphens → underscores (Python identifiers) | acme_corp_apis |
| Rust module paths | Hyphens → underscores (Rust identifiers) | acme_corp_ |
| C++ namespaces | Hyphens → underscores (C++ identifiers) | acme_corp:: |
| Java packages | Hyphens → dots (reverse-domain convention) | com.acme.corp.apis |
Given org=Acme-Corp and API path proto/payments/ledger/v1:
| Coordinate | Derived Value |
|---|---|
| Go module | github.com/Acme-Corp/apis/proto/payments/ledger |
| Py dist | acme-corp-payments-ledger-v1 |
| Py import | acme_corp_apis.payments.ledger.v1 |
| Crate | acme-corp-payments-ledger-v1-proto |
| Rust mod | acme_corp_payments::ledger::v1 |
| Conan | acme-corp-payments-ledger-v1-proto |
| C++ ns | acme_corp::payments::ledger::v1 |
| Maven | com.acme.corp.apis:payments-ledger-v1-proto |
| Java pkg | com.acme.corp.apis.payments.ledger.v1 |
| npm | @acme-corp/payments-ledger-v1-proto |
Key takeaway: Hyphens in org names are fine. APX handles the per-ecosystem translation automatically. You never need to pre-normalize your org name.
Go Workflow¶
Go is the Tier 1 language with the most mature overlay system:
apx gen gogenerates code intointernal/gen/go/with a synthesizedgo.moddeclaring the canonical module path.apx syncaddsusedirectives togo.workso the Go toolchain resolves canonical imports to local overlays.- When ready for production,
apx unlinkremoves the overlay andgo getadds the released module. Import paths stay the same.
Python Workflow¶
Python uses editable installs (pip install -e) as the local resolution mechanism:
apx gen pythonscaffolds each overlay as an installable Python package withpyproject.tomland namespace__init__.pyfiles.apx link pythonrunspip install -efor each overlay into the active virtualenv.apx unlinkremoves the overlay;pip install <dist-name>adds the released package. Import paths stay the same.
Java Workflow (Maven-Native)¶
Java uses Maven's dependency resolution and code generation phases:
- Producer releases schema artifacts (proto files packaged as a jar/zip) to a Maven repository via APX's release pipeline.
- Consumer adds the schema artifact as a Maven dependency using the derived coordinates (
com.<org>.apis:<domain>-<name>-<line>-proto). - Maven's
generate-sourcesphase usesprotobuf-maven-plugin(or equivalent) to generate Java code from the schema artifact intotarget/generated-sources/. - For local development,
apx link java(planned) installs schema artifacts to~/.m2/repository, allowing Maven to resolve them without a remote repository.
Java developers never interact with Go modules or go.work. The Maven coordinate system provides a complete, self-contained experience.
TypeScript Workflow¶
TypeScript uses npm packages as the published artifact with scoped package names:
- Producer releases schema artifacts to an npm registry via APX's release pipeline. The npm package name is deterministically derived:
@<org>/<domain>-<name>-<line>-proto. - Consumer installs the package using
npm install @<org>/<domain>-<name>-<line>-proto. - For local development,
apx link typescript(planned) links local schema artifacts vianpm link, allowing resolution without a remote registry. apx unlinkremoves the local link;npm install @<org>/<pkg>adds the released package. Import paths stay the same.
TypeScript developers import generated types directly from the npm package name: