Tips to Write Optimized and Secure Move Code on Aptos: A Practical Guide

In this blog, we'll walk through practical tips and best practices that will elevate your Move development skills.
1. Master the Power of Resources
Resources are linear types that represent assets or state. They cannot be copied or discarded accidentally. Use them to store key user-specific data like balances or ownership records.
Use move_to
and borrow_global_mut
to manage these resources effectively.
👉 Tip: All your storage logic and entry functions should be organized inside a Move module. Think of a module as the smart contract that holds your logic, structs, and public interfaces.
Example:
2. Always Declare acquires
When your function interacts with global storage, explicitly declare the resource in the acquires
clause. This helps the Move VM verify resource safety.
3. Reduce Storage Access Overhead
Reading from global storage costs gas. Avoid accessing the same resource multiple times.
❌ Inefficient:
✅ Efficient:
4. Use Checked Math
Avoid overflows by using checked_add
, checked_sub
, and checked_mul
. These functions return Option
and ensure math safety.
let new_amount = a.checked_add(b).expect("overflow");
5. Avoid Vectors in Global Storage
Vectors are not ideal for global storage as they require copying the entire vector on modification. Prefer resource-per-address mappings for user data.
6. Minimize Resource Footprint
Store only necessary fields. Instead of storing complete objects, store references or IDs where possible. Smaller resources = cheaper reads and writes.
7. Use Constants and Error Codes
Centralize all error codes as constants and use them in assertions.
const E_NOT_INITIALIZED: u64 = 1; assert!(exists<Balance>(addr), error::invalid_argument(E_NOT_INITIALIZED));
8. Safe Initialization Patterns
Ensure users initialize their resources with an initialize()
function before any interaction
.
9. Gas Optimization Summary
Here are some easy ways to write code that uses less gas:
✨ Tip: Only check exists<T>
once and store the result in a variable instead of checking it again.
✨ Tip: Don’t borrow the same resource more than once .Just use a let
variable to reuse it.
✨ Tip: Keep your main entry functions clean by minimizing complex if or match branches.
✨ Tip: Store only essential data in smaller resource structs reduce gas costs on reads and writes.
10. Security Best Practices
Use
signer::address_of
to check identityValidate all function inputs
Restrict
mint
,burn
, ortransfer
accessUse clear assertion messages
11. Add Events for Transparency
Events improve transparency and enable off-chain analytics.
struct MintEvent has drop, store { amount: u64, } public fun mint(...) { emit_event<MintEvent>(&event_handle, MintEvent { amount }); }
12. Unit Testing is Essential
Write unit tests using #[test]
for every entry and private function.
Run tests with:
aptos move test
Cover edge cases and expected errors to prevent regressions.
13. Upgrade with Feature Flags
Use a version
constant or config resource to control logic upgrades gracefully.
const VERSION: u64 = 1;
14. Separate Dev Modules
Keep main modules production-safe. Place debug functions and test mints in a separate module that’s only deployed on devnet or testnet.
Conclusion
Writing optimized Move code takes practice and attention to detail. By applying these tips from minimizing storage reads to writing secure, testable logic ,you’ll produce efficient and secure smart contracts ready for real-world Aptos deployments.
Want more tips? Follow this blog or reach out with your Move questions!
Subscribe to my newsletter
Read articles from Hari priya directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Hari priya
Hari priya
I’m a Web3 developer exploring the Aptos blockchain and Move language. I write beginner friendly guides and share hands on tips to help others build smart contracts and dApps.