Best Practices & Common Pitfalls

43.1 Code style

Sui: use the 2024 edition, module labels (module pkg::name;), method syntax, public(package) instead of friend, and macros instead of hand-written index loops.

Aptos: use Move 2 features (receiver calls, index notation, enums, for loops), module events (#[event]), objects and the Fungible Asset standard for new code.

Naming: modules and functions snake_case; structs and enums PascalCase; constants UPPER_SNAKE; errors EPascalCase (Sui) or E_UPPER_SNAKE (Aptos); the OTW is the module name in UPPERCASE.

Getter functions named after the field (fun value(self: &Counter): u64), setters set_*.

Document every public function with /// doc comments, including abort conditions.

Group code: imports, constants/errors, structs, init, public functions, package functions, private helpers, test-only code.

Format with the Move formatter (Prettier plugin on Sui) and run linters (sui move build --lint, aptos move lint).

43.2 Design guidelines

Give types the minimum abilities needed Abilities are permissions; each one widens what others can do with your type

Sui: return objects from public functions instead of transferring inside Composability in PTBs

Sui: prefer owned objects for per-user state Fast path, no contention

Aptos: prefer per-user resources/objects over global maps Use capabilities, not hard-coded addresses, for admin rights Transferable, revocable, testable Keep init/init_module minimal Cannot be re-run

Plan for upgrades from day one (versions, dynamic fields for extensions) Struct layouts are frozen

Use events for everything off-chain systems need Indexers depend on them Use tables for unbounded data Object/resource size and gas limits

Test failure paths as thoroughly as success paths Aborts are your security boundary

43.3 Common pitfalls

Forgetting let mut (Sui 2024) "Invalid mutable borrow / assignment" errors Declare let mut x or mut parameter Missing drop on a value at scope end "unused value without drop"

Consume it (transfer, destroy, return) or add drop if appropriate

Using transfer::transfer from another module "Invalid private transfer"

Use public_transfer (type needs store) or call the defining module Sharing an existing owned object Abort / compile error

Share only freshly created objects in the same transaction Mixing u64 and u128 Explicit as casts Overflow in a * b / c

Widen to u128/u256 before multiplying Hard-coding package IDs in code Breaks across networks/upgrades

Use named addresses, config objects, type_name Unbounded vector in a shared object Gas/size blowups Table/Bag, pagination

Forgetting acquires (older Aptos compilers) Add acquires T or upgrade compiler move_to twice RESOURCE_ALREADY_EXISTS abort Check exists<T> first Expecting init to run on upgrade Missing state after upgrade Admin-gated migration function Public randomness function

Private entry (#[randomness] on Aptos) Deleting a parent with dynamic fields Orphaned, inaccessible data Remove fields first Treating string::length as characters Wrong lengths for non-ASCII It counts bytes Depending on events in Move logic Store state in objects/resources

Assuming old Sui package versions stop working Users call old logic Version checks on shared objects Comparing coin types loosely Accepting worthless coins

Phantom types, concrete Coin<SUI>, TypeName checks