Developer best practices
This guide outlines essential development best practices for building on the Internet Computer Protocol (ICP).
Best practices
Recommendation: Generous canister top-up.
Ensure all canisters have sufficient cycles to sustain operations for several years initially. Given the cost-effectiveness of storage and computation on ICP compared to other platforms, this upfront investment is usually modest.
Refer to topping up cycles page to learn how to top-up cycles effectively.
Refer to the cycles estimate page to estimate cycle usage.
Recommendation: Manage cycle depletion.
ICP features a mechanism to prevent canisters from running out of cycles. Canisters have a configurable freezing_threshold
, dynamically evaluated in cycles. Set freezing_threshold
conservatively, ensuring at least 90 to 180 days' worth of cycles for proactive management.
Refer to this resource to learn how to configure the freezing_threshold
of a canister.
To make sure you won’t get surprised by a high cycle burn rate or hitting an instruction limit, you can use the recently added performance counter API to profile your canisters even before going live.
Recommendation: Efficient implementations.
Optimize canister implementation to avoid unnecessary costs.
Use canister timers over plain heartbeats for reduced daily costs.
Motoko
- Opt for
TrieMap
overHashMap
to prevent automatic resizing overhead. - Utilize
Buffer
instead ofArray
for dynamic resizing. - Consider
Blob
over[Nat8]
for compact storage and reduced GC pressure:
- Use
Blob
instead of[Nat8]
for storing large binary assets. - Use
Blob
instead of[Nat8]
when sending or receiving Candidvec nat8/blob
values. The choice is yours butBlobs
are 4x more compact and much less taxing on garbage collection (GC).
- Store large Blobs in stable memory for efficient manual management.
- Use the
compacting-gc
setting, especially in append-only scenarios, to allow access to larger heaps and reduce the cost of copying large, stationary objects. - Be mindful of calling an [actor class]((/docs/current/motoko/main/writing-motoko/actor-classes), as the overhead is similar to that of installing a fresh canister.
Rust
- Exercise caution with
Vec<u8>
andString
types for state serialization. Due to Rust limitations, using these types would increase the number of instructions required to encode or decode the message - Refer to recommended blogs and articles for effective Rust canister development.
Resources
Several resources on efficient implementations include:
- Blog post on effective Rust canisters by Roman Kashitsyn
- Good practices for canister development by Joachim Breitner
Recommendation: State backup and restoration.
Implement backup mechanisms within canisters for state protection against deallocation or upgrade issues. Explore backup strategies like those employed by Distrikt.
Recommendation: Transaction history storage.
If your application needs to store transaction history, consider using dedicated services like CAP for maintaining transaction logs. This facilitates integration with explorers and wallets and aids in ownership state reconstruction if the main canister is compromised. Be mindful of additional costs associated with inter-canister calls.