> For the complete documentation index, see [llms.txt](https://docs.rumi.systems/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rumi.systems/rumi-cli/commands/dev.md).

# dev

> **⚠️ In Development**: The `rumi dev` command is currently in development. The content in this section is subject to change.

The `rumi dev` commands incrementally evolve an existing Rumi application: adding, removing, and inspecting microservices, model types, connectors, and configuration, without hand-editing XML or writing boilerplate.

## Command Structure

Every command follows the same shape:

```bash
rumi dev <category> <action> [options]
```

Where `<category>` is the kind of thing being managed and `<action>` is one of `list`, `get`, `add`, `remove`, or a category-specific verb.

## Categories

| Category     | Manages                                                  |
| ------------ | -------------------------------------------------------- |
| `app`        | Inspect scaffolded Rumi apps                             |
| `service`    | Microservices (processor, driver, connector, webservice) |
| `handler`    | `@EventHandler` methods in a microservice's `Main.java`  |
| `message`    | X-ADML message type definitions                          |
| `entity`     | Embedded entity definitions in the message model         |
| `state`      | X-ADML state entity definitions                          |
| `field`      | Fields on a message or entity                            |
| `collection` | X-ADML collection declarations                           |
| `operation`  | API operations in a microservice's `api.xml`             |
| `connector`  | Custom connectors snapped into a microservice            |
| `config`     | X-DDL config fragments and schema validation             |
| `factory-id` | Global factory ID inspection                             |

Run `rumi dev <category> --help` for a category's actions and options.

## Common Options

These recur across the commands:

| Option       | Short | Description                                                                        |
| ------------ | ----- | ---------------------------------------------------------------------------------- |
| `--app-root` | `-a`  | Application root. Required, and must be an absolute path                           |
| `--field`    | `-f`  | Repeatable field spec, `name:type[:modifier...]`                                   |
| `--attr`     |       | Repeatable type-level attribute, `key=value`                                       |
| `--scope`    |       | Which model the edit lands in. See [Model Scope](#model-scope)                     |
| `--force`    |       | Override a referential-safety block. See [Referential Safety](#referential-safety) |
| `--dry-run`  |       | Report what would change without writing                                           |
| `--json`     |       | Emit structured output                                                             |

Every mutating command reports a change set, so you can see exactly which files were touched:

```bash
$ rumi dev message add processor -n PlaceOrder -f "id:String,qty:int" -a /path/to/app
files modified:
  * /path/to/app/rumi-quickstart-processor/src/main/models/.../messages.xml
```

## Field Specs

Fields are given as `name:type`, with optional modifiers:

```bash
--field id:String:key          # 'key' is shorthand for key=true
--field qty:int                # a plain scalar
--field id:String:key,qty:int  # several in one flag
```

Field types are **canonicalized to their ADML scalar names** on write, so the Java primitive spellings you are used to are accepted and stored canonically. `int` becomes `Integer`, `double` becomes `Double`, `string` becomes `String`. Names that are not recognized scalar aliases, such as entity and message references, pass through untouched.

```bash
$ rumi dev field add processor PlaceOrder -n price -t double -a /path/to/app
$ rumi dev message get processor PlaceOrder -a /path/to/app
fields:
  id : String {id=1, name=id, type=String}
  qty : Integer {id=2, name=qty, type=Integer}
  price : Double {id=3, name=price, type=Double}
```

## Model Scope

`--scope` selects which model file an edit lands in:

| Scope              | Model                                                       |
| ------------------ | ----------------------------------------------------------- |
| `service-messages` | The microservice's own message model                        |
| `service-state`    | The microservice's state model                              |
| `roe-messages`     | The application-wide ROE model, shared across microservices |

The default is `service-messages` for messages, entities, and fields, and `service-state` for collections.

Use `roe-messages` for a message several microservices exchange. Note where the change lands:

```bash
$ rumi dev message add processor -n OrderPlaced --scope roe-messages -f "id:String" -a /path/to/app
files modified:
  * /path/to/app/rumi-quickstart-roe/src/main/models/.../messages.xml
```

## Referential Safety

Removing a type that is still referenced is blocked, and the error names every referrer:

```bash
$ rumi dev entity remove processor -n Address -a /path/to/app

**** cannot remove entity 'Address': still referenced by field 'shipTo' on message 'PlaceOrder' (remove the references first, or force the removal)
```

Remove the references first, or pass `--force` to remove anyway. Forcing leaves the referring fields pointing at a type that no longer exists, so prefer removing the references.

## IDs Are Never Reused

Model types, fields, and collections each carry an ID that identifies them on the wire, and removing one never returns its ID to the pool. A removed element leaves a reserved tombstone behind:

```xml
<!-- id=3 reserved -->
```

This is deliberate: recycling the ID of a removed element would let a peer running the older model misinterpret the new one. Model element IDs are allocated high-water-mark plus one. Factory IDs take the lowest ID that has never been allocated, skipping reserved ones, so `rumi dev factory-id next` can appear to skip numbers. That is correct behaviour rather than a defect.

## Categories in Detail

### app

```bash
rumi dev app list [-u|--under DIR] [--json]      # find scaffolded apps
rumi dev app info -a|--app-root ROOT [--json]    # app metadata
```

### service

```bash
rumi dev service list -a ROOT [--json]
rumi dev service get <name> -a ROOT [--json]
rumi dev service add <name> -t|--type TYPE -a ROOT
       [-h|--ha-model MODEL] [--clustered] [-P|--partitions N] [--json]
rumi dev service remove <name> -a ROOT [--dry-run] [--json]
```

`TYPE` is `processor`, `driver`, `connector`, or `webservice`. The HA model applies to the clusterable types, `processor` and `webservice`, and is `STATE_REPLICATION` (default) or `EVENT_SOURCING`.

`add` has no `--dry-run`: it writes a whole module subtree, scripts, and config fragments, and the underlying builder has no preview mode. Scaffold into a throwaway app if you want to inspect the result first.

### message

```bash
rumi dev message list <service> -a ROOT [--json]
rumi dev message get <service> <name> -a ROOT [--json]
rumi dev message add <service> -n NAME [-f FIELD]... [--scope SCOPE] -a ROOT
rumi dev message remove <service> -n NAME [--force] [--scope SCOPE] -a ROOT
```

### entity

Embedded entities in the message model. For a microservice's state entities, use [`state`](#state).

```bash
rumi dev entity list <service> [--scope SCOPE] -a ROOT [--json]
rumi dev entity get <service> <name> [--scope SCOPE] -a ROOT [--json]
rumi dev entity add <service> -n NAME [-f FIELD]... [--attr K=V]... [--scope SCOPE] -a ROOT
rumi dev entity remove <service> -n NAME [--force] [--scope SCOPE] -a ROOT
```

### state

```bash
rumi dev state list <service> -a ROOT [--json]
rumi dev state get <service> <name> -a ROOT [--json]
rumi dev state add <service> -n NAME [-f FIELD]... [--attr K=V]... -a ROOT
rumi dev state remove <service> -n NAME [--force] -a ROOT
```

`--attr` sets entity-level attributes:

```bash
rumi dev state add processor -n Order -f "id:String:key" --attr transactional=true -a /path/to/app
```

### field

Every action names both the microservice and the owning message or entity.

```bash
rumi dev field add <service> <type> -n NAME -t TYPE [--attr K=V]... [--scope SCOPE] -a ROOT
rumi dev field delete <service> <type> -n NAME [--scope SCOPE] -a ROOT
rumi dev field deprecate <service> <type> -n NAME [--scope SCOPE] -a ROOT
rumi dev field rename <service> <type> -n NAME --to NEW [--scope SCOPE] -a ROOT
```

`deprecate` marks a field deprecated while keeping it on the wire, which is the compatible way to retire one. `delete` removes it outright and retires its ID. `rename` keeps the ID, so it stays wire-compatible.

### collection

```bash
rumi dev collection list <service> -a ROOT [--json]
rumi dev collection get <service> <name> -a ROOT [--json]
rumi dev collection add <service> -n NAME --is KIND --contains TYPE
       [--attr K=V]... [--scope SCOPE] -a ROOT
rumi dev collection remove <service> -n NAME [--scope SCOPE] -a ROOT
```

`--contains` is a verbatim type reference: it names an entity or message type exactly as spelled in the model and is not canonicalized.

### operation

```bash
rumi dev operation list <service> -a ROOT [--json]
rumi dev operation get <service> <name> -a ROOT [--json]
rumi dev operation add <service> -n NAME --in MESSAGE [--out MESSAGE]
       [--rest-path PATH] [--rest-method METHOD] -a ROOT
rumi dev operation remove <service> -n NAME -a ROOT
rumi dev operation rename <service> -n NAME --to NEW -a ROOT
```

Omit `--out` for a one-way operation.

### connector

A connector is a message-bus binding to an external system, and can be snapped into any microservice regardless of its type. This is distinct from the `connector` microservice type that [`rumi quickstart connector`](/rumi-cli/commands/quickstart/connector.md) scaffolds.

```bash
rumi dev connector list <service> -a ROOT [--json]
rumi dev connector get <service> <name> -a ROOT [--json]
rumi dev connector add <service> -n NAME -a ROOT
rumi dev connector remove <service> -n NAME -a ROOT
```

`add` generates the connector skeleton class and the `connector://` bus binding that wires it into the microservice.

### handler

```bash
rumi dev handler list <service> -a ROOT [--json]
rumi dev handler get <service> <method> -a ROOT [--json]
rumi dev handler add <service> -m|--method NAME -M|--message TYPE
       [-b|--body CODE] -a ROOT [--dry-run] [--json]
rumi dev handler remove <service> -m|--method NAME -a ROOT [--dry-run] [--json]
```

`--body` is optional; the handler gets an empty TODO body when it is omitted.

### config

```bash
rumi dev config list -a ROOT [-p|--profile NAME] [--json]
rumi dev config get -a ROOT
rumi dev config add -s|--scope a/b/c -x|--xml FILE|- -a ROOT [--dry-run] [--json]
rumi dev config remove -s|--scope a/b/c [-t|--tag TAG] [-n|--name NAME]
       -a ROOT [--dry-run] [--json]
rumi dev config validate -a ROOT [--json]
```

`get` prints the rendered `config.xml`. `add` takes the fragment from a file or from stdin with `-`. `remove` needs at least one of `--tag` or `--name`. `validate` exits non-zero on any ERROR or FATAL; a warning-only run exits 0.

### factory-id

```bash
rumi dev factory-id list -a ROOT [--json]    # map each used ID to its owner
rumi dev factory-id next -a ROOT [--json]    # lowest unused, non-reserved ID
```

## Common Workflows

### Building out a microservice

```bash
# 1. Add the microservice
rumi dev service add order-processor -t processor -a /path/to/app

# 2. Model the messages it exchanges
rumi dev message add order-processor -n PlaceOrder -f "id:String,qty:int" -a /path/to/app

# 3. Add state for it to keep
rumi dev state add order-processor -n Order -f "id:String:key,qty:int" -a /path/to/app

# 4. Handle the message
rumi dev handler add order-processor -m onPlaceOrder -M PlaceOrder -a /path/to/app
```

### Evolving a model compatibly

```bash
# Add a field; it gets the next ID
rumi dev field add order-processor PlaceOrder -n price -t double -a /path/to/app

# Retire one without breaking older peers
rumi dev field deprecate order-processor PlaceOrder -n qty -a /path/to/app
```

### Sharing a message between microservices

```bash
rumi dev message add order-processor -n OrderPlaced --scope roe-messages \
    -f "id:String" -a /path/to/app
```

### Reaching an external system

```bash
rumi dev connector add order-processor -n auditFeed -a /path/to/app
```

## Best Practices

1. **Preview first** - `--dry-run` shows the change set without writing.
2. **Deprecate rather than delete** - keeps peers on the older model working.
3. **Put shared messages in ROE scope** - a message two microservices exchange belongs in `roe-messages`, not in one of them.
4. **Remove references before types** - prefer that to `--force`.
5. **Use version control** - commit after each change, so a change set is easy to review.

## Related Documentation

* [quickstart](/rumi-cli/commands/quickstart.md) - Generate a new application to evolve
* [set](/rumi-cli/commands/set.md) - CLI settings, including full stack traces on failure
* [Core Documentation](/rumi-core/guides/developing-applications.md) - Developing Rumi applications
