# Aleo Leo Programs in Practice **Published by:** [Colliseum](https://paragraph.com/@colliseum/) **Published on:** 2025-04-21 **URL:** https://paragraph.com/@colliseum/aleo-leo-programs-in-practice ## Content Hi, my name is Heorhii, and I’ll guide you through working with Leo programs on Aleo—beyond the basics. Let’s explore how to use mappings effectively, understand function types, and structure your program to fit the constraints of the Aleo environment. Understanding mapping operations. Mappings in Aleo are a form of on-chain key-value storage. You’ll often use them to track balances or user-specific state. These operations are only allowed within async functions and must follow a few defined patterns:get – Retrieves a value from a mapping. Fails if the key doesn't exist.get_or_use – Retrieves the value or initializes it with a default if the key is missing.set – Updates a value in the mapping.contains – Checks if a key exists.remove – Deletes a key-value pair.Here's a basic example of updating a user counter:program test.aleo { mapping counter: address => u64; async transition dubble() -> Future { return update_mappings(self.caller); } async function update_mappings(addr: address) { let current_value: u64 = Mapping::get_or_use(counter, addr, 0u64); Mapping::set(counter, addr, current_value + 1u64); let new_value: u64 = Mapping::get(counter, addr); Mapping::set(counter, addr, new_value + 1u64); } } Working with functions in Leo. Leo supports several types of functions. Each type has specific usage rules and visibility: 1. Transition functions. These are your main entry points and can interact with the Aleo blockchain.transition foo(public a: field, b: field) -> field { return a + b; } 2. Helper functions. Used internally and do not involve records or state changes.function add(a: field, b: field) -> field { return a + b; } 3. Inline functions. Great for simple logic. They’re inlined by the compiler and cannot be called directly.inline square(x: u64) -> u64 { return x * x; } 4. Async functions. Used for asynchronous, on-chain computation. Typically called from async transition functions.async transition transfer_public_to_private(receiver: address, public amount: u64) -> (token, Future) { let new: token = token { owner: receiver, amount }; return (new, update_public_state(self.caller, amount)); } async function update_public_state(public sender: address, public amount: u64) { let balance: u64 = Mapping::get_or_use(account, sender, 0u64); Mapping::set(account, sender, balance - amount); } Keeping constraints in mind. When building with Leo, you need to consider limitations defined by snarkVM:Max program size: 100 KBMax mappings: 31Max imports: 64Max functions: 31Max transaction size: 128 KBMax compute budget: 100,000,000 microcreditsIf you hit these limits, try modularizing your code or simplifying your data structures. All these parameters exist to ensure on-chain execution remains efficient and scalable. Learn more: https://docs.leo-lang.org/language/programs Final thoughts. As you begin developing with Leo, keep in mind how function visibility, mapping behavior, and on-chain constraints shape your architecture. The async pattern and use of mappings are essential for building any useful ZK-powered app on Aleo. I hope this article helps you move from writing basic transitions to building real-world ZK apps. More articles coming soon! Let me know what you'd like me to explore next.SubscribeTo know more about Aleo, join now!Aleo TwitterAleo DiscordAleo WebsiteList of Aleo and Leo code and resoursesPrepared by Colliseum ## Publication Information - [Colliseum](https://paragraph.com/@colliseum/): Publication homepage - [All Posts](https://paragraph.com/@colliseum/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@colliseum): Subscribe to updates - [Twitter](https://twitter.com/HeorhiiY): Follow on Twitter