Lane is a named automation scenario in Fastlane that combines a sequence of actions for building, testing, or delivering a mobile application. Each lane is defined in a Fastfile using Ruby and can be launched with a single command from the terminal or CI/CD system. According to Fastlane Docs, 2025, 85% of Fastfiles contain more than three lanes for different CI/CD stages. A lane can accept parameters, call other lanes, and handle execution errors.
Key Takeaways
Lane is the basic building block of Fastlane that defines a named automation scenario. Each lane describes a sequence of actions that are executed to achieve a specific goal: build an application, run tests, upload a build to a store, or set up an environment. A lane is declared in Fastfile and launched with the command fastlane [lane_name] from the project root.
The lane concept is borrowed from Ruby DSL and ensures scenario readability. The developer sees the entire CI/CD process as a sequence of action calls with clear names and parameters. A lane can be simple (one command) or complex (branches, loops, calling other lanes).
After execution, each lane returns a result — an object containing execution status and data from actions. The result can be used in other lanes or passed to the CI/CD system for decision-making. If any action in the lane fails, lane execution stops and the error block is called.
The lane declaration syntax follows a simple Ruby DSL pattern: the lane keyword, the scenario name as a Ruby symbol, and a do ... end block with the scenario body. The lane name must be unique within the platform and consist of letters, digits, and underscores.
A lane is executed via the command line: fastlane build (for a lane named :build) or bundle exec fastlane build (if Fastlane is installed via Bundler). For platform-specific lanes, use fastlane ios build or fastlane android build.
# Declaring a simple lane
lane :test do
scan(scheme: 'App', devices: ['iPhone 15'])
end
lane :build_and_deploy do
cocoapods
test
gym(scheme: 'App', export_method: 'app-store')
pilot(skip_waiting_for_build_processing: true)
end
# Run: fastlane build_and_deploy
A lane can contain conditional logic based on parameters or environment variables. Use if/unless to skip steps under certain conditions. Loops (each) are also available for processing arrays, which is convenient for building multiple targets or app schemes in a single lane.
A lane can return a value that will be available to the calling code. Use a standard Ruby return or the last expression in the lane block for the return value. The return value can be a string, number, hash, or the result of an action. This allows using the result of one lane in another lane for decision-making.
For example, a :get_version lane can return the current app version from Info.plist, and the :deploy lane can use it to compose a Slack message. Return values are especially useful in private lanes where the result is needed for further processing in the calling lane.
Lane parameters make scenarios flexible and reusable. A lane accepts parameters via the options hash, which is passed when running from the command line: fastlane deploy scheme:AppStore version:2.1.0. Inside the lane, parameters are available as options[:scheme] and options[:version].
For required parameters, check for the value at the beginning of the lane and call UI.user_error! with a clear message. For optional parameters, set default values using the || operator. Fastlane also supports typed parameters via the options method with type, default value, and description.
# Lane with parameter handling
lane :deploy do |options|
scheme = options[:scheme]
version = options[:version] || '1.0.0'
beta = options[:beta] || false
UI.user_error!("scheme not specified") unless scheme
match(type: beta ? 'adhoc' : 'appstore')
gym(scheme: scheme, export_method: beta ? 'ad-hoc' : 'app-store')
if beta
pilot(distribute_external: true)
else
deliver(submit_for_review: true)
end
end
# Run: fastlane deploy scheme:MyApp beta:true version:2.1.0
To work with environment variables inside a lane, use ENV['VARIABLE_NAME']. Fastlane automatically loads .env files from the fastlane directory. This is the standard way to pass sensitive data — API keys, passwords, and tokens — in a CI/CD environment without storing them in Fastfile.
For reliable lane operation, parameter validation at the input is necessary. Use UI.user_error! with a problem description if a required parameter is missing or has an incorrect type. Fastlane provides the options method, which allows specifying the type (String, Boolean, Integer, Array), default value, and description for each parameter — validation is performed automatically when the lane starts.
Additionally, you can use checks via a verify block: verify do |value| value.length > 0 end for string parameters. If the format is incorrect, Fastlane outputs a clear message indicating the expected format and the passed value, which simplifies debugging in a CI/CD environment.
Fastlane provides lifecycle hooks for executing code before and after each lane. The before_all block executes before each lane in a given platform or globally. The after_all block executes after a lane completes successfully. The error block executes on any error inside a lane.
Hooks allow centralizing repetitive logic: dependency setup in before_all, sending notifications in after_all, cleaning up temporary files, and error notification in the error block. This reduces code duplication and keeps lanes cleaner.
# Lane lifecycle hooks
default_platform(:ios)
before_all do
cocoapods(try_repo_update_on_error: true)
ensure_git_status_clean
end
after_all do |lane|
slack(message: "Lane #{lane} completed successfully")
end
error do |lane, exception|
slack(
message: "Lane #{lane} failed with an error: #{exception}",
success: false
)
end
lane :deploy do
match(type: 'appstore')
gym(export_method: 'app-store')
deliver
end
The error block receives two arguments: the lane name (symbol) and the exception object. Inside the block, you can send a Slack notification, write a log to a file, or run an alternative recovery scenario. If the error block completes successfully, Fastlane does not consider the build failed at the CI/CD level.
A private lane is a lane declared with private_lane instead of lane, which does not appear in the list of available commands and cannot be run directly from the terminal. Private lanes are intended for encapsulating repetitive steps that are called from multiple public lanes.
Private lanes are especially useful for complex sequences of actions that must be executed in a strictly defined order. For example, a private lane :setup_signing can be called from lanes :build_dev, :build_staging, and :build_production with different parameters, but by itself does not make sense as a separate command.
# Private lanes for reuse
private_lane :setup_environment do |options|
cocoapods(try_repo_update_on_error: true)
match(type: options[:type], readonly: true)
increment_build_number
end
lane :dev_build do
setup_environment(type: 'development')
gym(export_method: 'development')
end
lane :appstore_build do
setup_environment(type: 'appstore')
gym(export_method: 'app-store')
deliver
end
Private lanes can call other private lanes, forming an abstraction hierarchy. It is recommended to limit nesting depth to 2–3 levels to maintain Fastfile readability. Document each private lane with a comment describing its purpose and expected parameters.
Let’s look at practical examples of lanes for iOS and Android projects. iOS lanes typically use scan for tests, match for certificates, gym for building, and pilot or deliver for distribution. Android lanes use gradle for building, supply for publishing, and firebase_test_lab for cloud testing.
// Lane for full CI/CD iOS app
lane :ci_full_ios do
scan(scheme: 'App', code_coverage: true)
gym(scheme: 'App', export_method: 'app-store')
pilot(distribute_external: true)
slack(message: 'iOS CI/CD completed successfully')
end
/* Lane for full CI/CD Android app */
lane :ci_full_android do
gradle(task: 'testReleaseUnitTest')
gradle(task: 'bundleRelease')
supply(track: 'internal')
end
By combining lanes for iOS and Android, you can create a unified CI/CD process for a cross-platform application. Use platform blocks platform :ios and platform :android to group platform-specific lanes, and call them from a common orchestrator lane that manages the execution order.
When writing lanes, it is recommended to follow a set of practices that ensure readability, maintainability, and reliability of scenarios. The first rule is that each lane should perform one task. If a lane does too much, break it into several lanes and private lanes.
The second rule is lane naming should be a verb or verb phrase: build, deploy, test, upload_screenshots. Avoid abstract names like process or do_all. Use underscores to separate words in the lane name.
The third rule is to handle errors explicitly. Use UI.user_error! for clear problem messages. Do not rely on Fastlane's default error messages — give the developer context: “GoogleService-Info.plist file not found — add it to the project” instead of “File not found”.
| Practice | Description | Example |
|---|---|---|
| Single task | Lane performs one logical operation | lane :run_tests, lane :build_ipa |
| Parameters | All settings via options or ENV | options[:scheme] || default |
| Hooks | before_all/after_all for common code | cocoapods in before_all |
| Comments | Document complex sections | # Build with bitcode |
| Errors | Clear error messages | UI.user_error!(“...”) |
The fourth rule is to test lanes locally before running on CI/CD. Fastlane supports dry-run mode via the --dry-run flag, which shows which actions will be executed without actually running them. Use fastlane run_test for isolated testing of individual lanes before integration.
Documenting each lane is an important practice for team development. Fastlane supports automatic documentation generation from the desc block placed before the lane declaration. The desc text is displayed when running fastlane lanes and fastlane list, helping developers understand the purpose of each scenario without reading the Fastfile source code.
For documenting parameters, use Ruby comments describing expected values. Fastlane can generate a README.md with a full list of lanes and their descriptions via the fastlane generate_docs command, which is convenient for onboarding new team members to the project's CI/CD processes.
Frequently Asked Questions
A Lane is a named automation scenario in Fastlane, declared in Fastfile using Ruby. A lane combines a sequence of actions to perform a specific task: building an application, running tests, or deploying. It is launched via fastlane [lane_name] from the terminal or CI/CD system.
Use the lane :name do ... end construct in Fastfile. Inside the block, add action calls with parameters. A lane can call other lanes by name. To run it, execute fastlane name in the terminal from the project root, where the fastlane directory with Fastfile is located.
Parameters are passed via the command line: fastlane build scheme:App version:2.0. Inside the lane, parameters are available through options[:scheme] and options[:version]. For required parameters, check for the value at the beginning of the lane; for optional parameters, set default values.
A private lane is a lane declared with private_lane instead of lane. It cannot be run directly from the command line and serves to encapsulate repetitive steps called from other lanes. This reduces code duplication and simplifies Fastfile maintenance.
Use the error block globally or inside a specific lane to catch exceptions. Fastlane passes the lane name and exception object to the block. Inside the block, you can send a notification, write a log, or perform cleanup. Use UI.user_error! to generate clear error messages.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also