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