# Developer Cheat Sheet

*[Documentation index](/llms.txt) · [Full index](/llms-full.txt)*

Quick reference on best practices for Sui Network developers.

## Move

Best practices for writing Move smart contracts on Sui.

### General

- Read the [Code Quality Checklist](https://move-book.com/guides/code-quality-checklist/) for best practices in Move development.
- Follow the [Move conventions](/develop/write-move/move-best-practices) for consistent naming and coding style.
- Use `vector`-backed collections (`vector`, `VecSet`, `VecMap`, `PriorityQueue`) with a known maximum size of 1000 items or fewer.
  - Use dynamic field-backed collections (`Table`, `Bag`, `ObjectBag`, `ObjectTable`, `LinkedTable`) for any collection that allows third-party addition, larger collections, and collections of unknown size.
  - Move objects have a maximum size of 250KB. Any attempt to create a larger object causes an aborted transaction. Ensure that your objects do not have an ever-growing `vector`-backed collection.
- If your function `f` needs a payment in, for example, SUI from the caller, use `fun f(payment: Coin<SUI>)` not `fun f(payment: &mut Coin<SUI>, amount: u64)`. This is safer for callers; they know exactly how much they are paying, and do not need to trust `f` to extract the right amount.

### Composability

- Use [Sui Object Display](https://move-book.com/programmability/display) to customize how your objects appear in wallets, apps, and explorers.
- Avoid self-transfers. When possible, return the object from the current function so that it can be used in a different command in a [programmable transaction block](/develop/transactions/ptbs/building-ptb).

### Package upgrades

- Read about [package upgrades](/develop/publish-upgrade-packages/upgrade) before publishing your package.
- Packages are immutable, so any published package can be called forever. Use [object versioning](/develop/publish-upgrade-packages/upgrade#versioned-shared-objects) to prevent older versions from being called.
- If you upgrade a package `P1` to `P2`, other packages and clients that depend on `P1` will continue using `P1`. They do not auto-update to `P2`. Both dependent packages and client code must be explicitly updated to point at `P2`.
- Packages that expect to be extended by dependent packages can avoid breaking their extensions with each upgrade by providing a standard (unchanging) interface that all versions conform to. See the [message sending](https://github.com/wormhole-foundation/wormhole/blob/74dea3bf22f0e27628b432c3e9eac05c85786a99/sui/wormhole/sources/publish_message.move) across a bridge example from Wormhole. Extension packages that produce outbound messages can use [`prepare_message`](https://github.com/wormhole-foundation/wormhole/blob/74dea3bf22f0e27628b432c3e9eac05c85786a99/sui/wormhole/sources/publish_message.move#L68-L90) from any version of the Wormhole package to produce a [`MessageTicket`](https://github.com/wormhole-foundation/wormhole/blob/74dea3bf22f0e27628b432c3e9eac05c85786a99/sui/wormhole/sources/publish_message.move#L52-L66) while client code that sends the message must pass that `MessageTicket` into [`publish_message`](https://github.com/wormhole-foundation/wormhole/blob/74dea3bf22f0e27628b432c3e9eac05c85786a99/sui/wormhole/sources/publish_message.move#L92-L152) in the latest version of the package.
  - `public` function signatures cannot be deleted or changed, but `public(package)` functions can. Use `public(package)` or private visibility liberally unless you are exposing library functions that will live forever.
  - It is not possible to delete `struct` types, change their definition, or add new [abilities](https://move-book.com/reference/abilities) through an upgrade. Introduce new types carefully because they live forever.

### Testing

- Use the [`sui::test_scenario`](https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/test/test_scenario.move) module to mimic multi-transaction, multi-sender test scenarios.
- Use the [`std::unit_test`](https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/move-stdlib/sources/unit_test.move) module for `assert_eq!` and `assert_ref_eq!` macros for better test error messages.
- Use the [`sui::test_utils`](https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/test/test_utils.move#L5) module for black-hole function `destroy`.
- Use the [`std::debug`](https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/move-stdlib/sources/debug.move) module for debug printing through `print`.
- Use `sui move test --coverage` to compute code coverage information for your tests, and `sui move coverage source --module <name>` to see uncovered lines highlighted in red. Push coverage all the way to 100% if feasible.

## Apps

- For optimal performance and data consistency, submit writes and reads to the same full node. In the TS SDK, use the wallet's [`signTransactionBlock`](https://sdk.mystenlabs.com/dapp-kit) API, then submit the transaction through a call to [`execute_transactionBlock`](/references/sui-api/sui-graphql/beta/reference/operations/mutations/execute-transaction) on your app's full node instead of using the wallet's `signAndExecuteTransactionBlock` API. This ensures read-after-write consistency. Reads from your app's full node reflect writes from the transaction right away instead of waiting for a checkpoint.
- Implement a local cache for frequently read data rather than over-fetching from the full node.
- Whenever possible, use programmable transaction blocks to compose existing onchain functionality rather than publishing new smart contract code. Programmable transaction blocks allow large-scale batching and heterogeneous composition, driving already-low gas fees down even further.
- Leave gas budget, gas price, and coin selection to the wallet. This gives wallets more flexibility, and the wallet is responsible for dry running a transaction to ensure it does not fail.

## Signing

- **Do not** sign two concurrent transactions that touch the same mutable owned object. Either use independent owned objects, combine related operations into one PTB, or wait for one transaction to conclude before sending the next one.
- Any `sui client` command that crafts a transaction (for example, `sui client publish` or `sui client call`) can accept the `--serialize-output` flag to output a base64 transaction for signing.
- Sui supports several signature schemes for transaction signing, including native multisig.

## zkLogin

- Call the proving service as sparingly as possible. Design your app flows such that you call the proving service only when the user is about to perform a real transaction.
- Beware of how you cache the ephemeral private key. Treat the private key as highly sensitive data, such as a password. If an unexpired ephemeral private key and its corresponding ZK proof are leaked, an attacker can steal the user's assets.
