Skip to content

Commit 30d194a

Browse files
committed
docs: add protocol development workflow guide
Document the end-to-end process for landing a protocol change across protocol, js-sdk-toolchain and godot-explorer, including the role of protocol-squad vs main and the backward compatibility contract.
1 parent 9284c38 commit 30d194a

1 file changed

Lines changed: 132 additions & 0 deletions

File tree

docs/PROTOCOL_DEV_WORKFLOW.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# Protocol Development Workflow
2+
3+
This document describes the end-to-end process for adding or changing something in the Decentraland protocol and rolling it out across the SDK and the explorer.
4+
5+
## Repositories involved
6+
7+
| Repo | Role |
8+
| --- | --- |
9+
| [`decentraland/protocol`](https://github.com/decentraland/protocol) | Protocol definition (protobuf). Source of truth for components, messages and RPCs. |
10+
| [`decentraland/js-sdk-toolchain`](https://github.com/decentraland/js-sdk-toolchain) | The Decentraland SDK. Consumes the protocol to generate the component definitions scenes use. |
11+
| [`decentraland/godot-explorer`](https://github.com/decentraland/godot-explorer) | The mobile explorer. Consumes the protocol to interpret scenes and render them. |
12+
13+
`js-sdk-toolchain` and `godot-explorer` communicate over the protocol, so any change made here must land in both before it reaches users.
14+
15+
## Branches and npm channels in `protocol`
16+
17+
| Branch / event | npm artifact | Audience |
18+
| --- | --- | --- |
19+
| Open PR | A per-PR CDN tarball (URL posted in the PR) | Local testing, draft PRs in dependent repos |
20+
| Merge to `main` | `@dcl/protocol@next` | Production. Consumed by SDK releases. |
21+
| Merge to `protocol-squad` | `@dcl/protocol@protocol-squad` | Iteration channel. Consumed by the explorer while a feature is being validated. |
22+
23+
### Why two branches?
24+
25+
`main` is production and **must not break compatibility**. Once a scene is deployed against a given protocol shape, that scene lives with that shape forever — we cannot retroactively patch deployed scenes.
26+
27+
`protocol-squad` exists so we can iterate on a feature (rename fields, tweak semantics, fix mistakes) without freezing those decisions into `main` and, transitively, into deployed scenes. Only when the shape is settled does it get cherry-picked to `main`.
28+
29+
**Invariant:** `protocol-squad` is always a superset of `main` and is always backward compatible with it. That guarantee is what lets us ship the explorer against `protocol-squad` to real users without breaking anything that targets `main`.
30+
31+
---
32+
33+
## Step-by-step: shipping a protocol change
34+
35+
Example scenario: adding a new component.
36+
37+
### 1. Open the protocol PR against `protocol-squad`
38+
39+
- Branch off `protocol-squad` in `decentraland/protocol`.
40+
- Add / modify the `.proto` definitions.
41+
- Open the PR with `protocol-squad` as the base.
42+
- CI will publish a per-PR tarball — copy that URL from the PR comment, you will need it in the next steps.
43+
44+
### 2. Open a `js-sdk-toolchain` PR pointing at the per-PR tarball
45+
46+
- Branch off `main` in `js-sdk-toolchain`.
47+
- Replace the `@dcl/protocol` dependency with the per-PR tarball URL from step 1.
48+
- Implement the SDK-side support for the new component (types, helpers, tests).
49+
- Open the PR. Do **not** merge yet.
50+
51+
### 3. Open a `godot-explorer` PR pointing at the per-PR tarball
52+
53+
- Branch off the explorer's default branch.
54+
- Point the protocol dependency at the same per-PR tarball from step 1.
55+
- Implement the explorer-side support (deserialization, rendering, systems).
56+
- Open the PR. Do **not** merge yet.
57+
58+
### 4. Validate end-to-end
59+
60+
With all three PRs wired to the per-PR tarball, verify the feature actually works:
61+
- A test scene built with the SDK PR renders correctly in the explorer PR.
62+
- Existing scenes still work (no regressions).
63+
- Iterate. If the protocol shape needs to change, push to the protocol PR — a new tarball will be published — and update the SDK / explorer PRs to consume it.
64+
65+
### 5. Merge the protocol PR into `protocol-squad`
66+
67+
Once the shape is validated:
68+
- Merge the protocol PR into `protocol-squad`.
69+
- CI publishes `@dcl/protocol@protocol-squad` with the new feature included.
70+
71+
### 6. Open a cherry-pick PR from `protocol-squad` to `main`
72+
73+
- Cherry-pick the merged commit onto a new branch off `main`.
74+
- Open a PR targeting `main`.
75+
- This is the moment to be conservative: anything that lands here is permanent for deployed scenes. If you are not 100% sure about a field name, default value, or semantics, **keep iterating on `protocol-squad` and delay the cherry-pick**.
76+
77+
### 7. Update the `godot-explorer` PR to use `@dcl/protocol@protocol-squad`
78+
79+
- Replace the per-PR tarball in the explorer PR with `@dcl/protocol@protocol-squad`.
80+
- Confirm CI is green.
81+
- Merge the explorer PR.
82+
83+
Because `protocol-squad` is always backward compatible with `main`, shipping the explorer against `protocol-squad` is safe for existing scenes.
84+
85+
### 8. Merge the cherry-pick into `protocol` `main`
86+
87+
- Merge the PR from step 6.
88+
- CI publishes `@dcl/protocol@next`.
89+
90+
### 9. Update the `js-sdk-toolchain` PR to use `@dcl/protocol@next`
91+
92+
- Replace the per-PR tarball in the SDK PR with `@dcl/protocol@next`.
93+
- Confirm CI is green.
94+
95+
### 10. Merge the `js-sdk-toolchain` PR
96+
97+
- Merge it into the SDK's default branch.
98+
99+
### 11. Release the SDK
100+
101+
- Cut an SDK release containing the new component.
102+
- Scene creators can now use it.
103+
104+
---
105+
106+
## Why this order matters: the backward compatibility contract
107+
108+
Scenes deployed to the world are immutable. If a scene was deployed using a component shape that turns out to be wrong, that scene will keep sending that wrong shape forever. We cannot patch it.
109+
110+
Consequences:
111+
- **The explorer can be fixed.** If the explorer mis-handles a shape, we ship a new explorer.
112+
- **The SDK can be fixed.** If the SDK generated bad code, we cut a new SDK and creators redeploy.
113+
- **A deployed scene cannot be fixed.** Whatever shape it serializes is what we are stuck with.
114+
115+
That asymmetry is the entire reason `protocol-squad` exists. It is the staging branch where we are allowed to be wrong. `main` is the branch where we are not.
116+
117+
### Rules of thumb
118+
119+
- Never merge a protocol change to `main` until the SDK and explorer have validated it end-to-end against the per-PR tarball or `@dcl/protocol@protocol-squad`.
120+
- Never name a field "temporary" — there is no such thing once it reaches `main`.
121+
- If something feels uncertain, leave it on `protocol-squad` for another iteration. Cherry-pick later.
122+
- The explorer can ship from `protocol-squad`; the SDK ships from `@dcl/protocol@next` (i.e. `main`). That is what enforces the invariant.
123+
124+
---
125+
126+
## Quick reference: which tag do I depend on?
127+
128+
| You are working in… | While iterating (PR open) | After feature lands |
129+
| --- | --- | --- |
130+
| `js-sdk-toolchain` | per-PR tarball URL | `@dcl/protocol@next` |
131+
| `godot-explorer` | per-PR tarball URL | `@dcl/protocol@protocol-squad` |
132+
| A scene (creator) | n/a | whichever SDK version is released |

0 commit comments

Comments
 (0)