| name | engine-expert |
| description | Use when implementing or fixing Zeebe engine code in zeebe/engine/ — BPMN process execution, DMN decision evaluation, job lifecycle, user/identity management, batch operations, variables, deployments, signals, messages, timers, multi-tenancy, or authorization. Also when modifying processors, event appliers, state classes, record value types, intents, or engine tests. |
Engine Expert
Reference for making changes to the Zeebe workflow engine with confidence. The engine processes thousands of commands per second on a single thread, state is rebuilt from the log on every restart, and partitions communicate over an unreliable network. Mistakes here cause leader/follower divergence, upgrade-path breakage, or production performance regressions. This skill exists to prevent the well-known classes of mistake.
Iron rules
These are summaries. The reference files (linked below) are authoritative — they carry the rationale, exceptions, and worked examples.
- No state mutation from processors. State changes only through events applied by event appliers.
- Released event appliers — and any method on a
Mutable*State interface or anything transitively called from an applier — must not change in logic. Add a new version/method instead. Cosmetic changes (formatting, imports, comments, behavior-equivalent renames) are fine. No golden file protects state class methods — extra care required.
- New event applier versions must reach every newer minor before its initial release. A version that ships only in an older minor's patch breaks upgrade replay — dead partitions, unrecoverable without an ad-hoc patch. See
event-appliers.md.
- Processors must end the command's processing by appending an event or a rejection. Exceptions are a last-resort rollback mechanism and are expensive — prefer pre-validation.
- Generated keys must be used as the record key of at least one appended record. Otherwise the key generator can't be rehydrated on replay and may hand out a duplicate.
- Inter-partition command receivers must be idempotent. Prefer reject-redundant-command over re-emitting events.
- Hot paths log at trace level only. INFO/DEBUG on a hot path is a performance regression at engine throughput.
- Values returned from
ColumnFamily.get(...) / state reads are backed by a shared, mutable buffer reused on the next read of the same column family. Never cache them or hold them across another read — copyFrom(...) if you need to keep the value, or use get(key, valueSupplier) to allocate a fresh instance.
- Non-transactional side effects (metrics, post-commit hooks) run only after all state and follow-up command writes that could throw. Exceptions roll back state, but already-executed side effects don't roll back — counters drift.
Where to read next
| If you are… | Read |
|---|
Designing or extending a record value type, intent, or ValueType | records.md |
| Editing or creating a processor, behavior class, validation, or rejection | processors.md |
Editing or creating an event applier, a Mutable*State interface method, or anything called from an applier | event-appliers.md |
| Writing or modifying engine tests | testing.md |
| Working with authorization resource types or permission types (which enum to use in which layer) | authz-enums.md |
Local checks before commit
Run only the tests relevant to your change — running the full engine test suite locally takes a long time and is what CI is for.
./mvnw license:format spotless:apply -T1C
./mvnw verify -pl zeebe/engine \
-Dtest='YourTest' -DskipTests=false -DskipITs -Dquickly
./mvnw verify -pl zeebe/engine -Dtest=NoChangesTest \
-DskipTests=false -DskipITs -Dquickly
Re-run any new or modified test at least 3× to catch flakiness. Push and let CI run the comprehensive suite.
Recommended workflow for new processor logic
- Write the test first, using
EngineRule and RecordingExporter against appended records (see testing.md). Run it — confirm it fails.
- Implement the processor (and its event applier + state changes).
- Re-run the test 3+ times. Then run the local checks block above.
Canonical docs