Part 2: Getting started with Melos — A practical guide for your Flutter Monorepo

David Cobbina
10 min readJul 14, 2024

--

This is Part 2, and we’re diving deeper into the world of Flutter mono repositories. But before we jump into Melos, the hero of this story, let’s make sure everyone’s on the same page.

If you’re new to mono repos and wondering why you might need Melos in the first place, head over to Part 1. It explores the benefits of mono repos and the challenges they can bring.

This article assumes you already have Flutter installed on your computer.
Now, assuming you’re ready to conquer those challenges with Melos, let’s get started!

Getting Started

To start using Melos, we need to install it as a global package so it can be used from anywhere on your system.

Installation

Lets run the command below to install melos

dart pub global activate melos

After running this command you see something like this

Setting Up a Melos Workspace

To set up a Melos workspace, let’s create a new folder for our mono repository.

Step 1: Create a Folder
Choose a descriptive name for your mono repo folder. This name will help identify your project easily. For example, we’ll use swiftcommerce in this guide.

Step 2: Create a pubspec.yaml File
Inside the swiftcommerce folder, create a file named pubspec.yaml. This file defines your project’s metadata and dependencies. Here’s the code you’ll need to add:

name: swift_commerce

environment:
# Dart SDK version that flutter project is compatible with
sdk: '>=3.0.0 <4.0.0'

dev_dependencies:
melos: ^4.1.0

This code snippet:

  • Sets the project name to swiftcommerce.
  • Specifies the compatible Dart SDK version (>=3.0.0 <4.0.0).
  • Installs Melos (^4.1.0) as a development dependency.

Step 3: Install Melos
Once you’ve saved the pubspec.yaml file, run the following command in your terminal to install Melos:

dart pub get

Step 4: Create a Melos Workspace configuration (melos.yaml)
Create a new file named melos.yaml at the root of your swiftcommerce folder. This file defines the configuration for your Melos workspace.

Step 5: Configure Melos Workspace (melos.yaml)
Open the melos.yaml file and add the following content:

name: swiftcommerce

packages:
- apps/**
- packages/**

This configuration:

  • Sets a name (swiftcommerce) for your workspace configuration.
  • Defines the workspace scope using the packages section.
  • apps/**: This instructs Melos to consider all subdirectories within the apps folder as packages.
  • packages/**: This instructs Melos to consider all subdirectories within the packages folder (or any other location) as packages.

What is Melos Workspace Scope ?

The combination of your directory structure and the packages section in melos.yaml determines the Melos workspace scope. This scope defines which code modules Melos manages for building, testing, and publishing.

Note: While using separate folders like apps and packages is a common convention, packages can reside anywhere within your mono repo. The melos.yaml configuration ultimately dictates which directories are considered packages by Melos.

Creating the Project Structure

  1. Create subdirectories: Inside the root directory, create the folders apps and packages.
  2. Inside apps create two new flutter applications called vendor app and customer app
  3. Inside packages create a new package called swiftcommerce_ui which will contain theming and reusable widgets
  4. After the creation the project should like the image below:

Bootstrapping your Melos workspace

Once you’ve set up your Melos workspace with the melos.yaml file and your project structure (including apps and packages folders, if applicable), you’ll need to bootstrap the workspace. Bootstrapping performs two key tasks:

Installing Dependencies: Melos scans your workspace based on the configuration in melos.yaml and installs all the dependencies listed in the pubspec.yaml files of your individual packages. This ensures all packages have their required dependencies to function properly.

Linking Packages: Melos also handles linking local packages within your workspace. This allows your applications within the apps folder to access and utilize the shared code modules defined in your packages folder (or any other location specified in the workspace configuration).

Running Melos Bootstrap

To bootstrap your Melos workspace, navigate to your mono repo’s root directory in your terminal and run the following command

melos bootstrap

OR

melos bs

Once successfully bootstrapped, you can develop your packages side-by-side with changes to a single package immediately reflecting across other dependent packages.

Versioning your packages

Before we take a look at how to version your packages using melos, we need to take a detour and jog our memory about semVer and convention commits (cheat sheet) .

What is SemVer?

SemVer is a widely adopted versioning scheme that defines a clear structure for package versions: Major.Minor.Patch. Here’s a breakdown of each component:

  • Major (X): Represents backward-incompatible changes. These changes might break existing code relying on the previous version. Incrementing the major version indicates developers need to adapt their code to work with the new version.
  • Minor (Y): Represents new features or functionalities added while maintaining backward compatibility. These changes should not require significant modifications in existing code using the package.
  • Patch (Z): Represents bug fixes or other non-breaking improvements within the existing features. Developers can safely upgrade to a new patch version without major code changes.

What is Conventional Commits

Conventional Commits provide a standardized format for crafting commit messages. This format benefits us in several ways:

  • Clarity: Commit messages become easier to understand, reflecting the exact nature of the change introduced.
  • Automation: Tools like Git hooks and linters can leverage the structure to automate tasks based on commit types.
  • Collaboration: Consistent commit messages enhance team communication and codebase maintainability.

The Commit Message Structure:
Conventional Commits define a specific structure for commit messages:

<type>[optional scope]: <description>
[optional body]
[optional footer(s)]

Here’s a breakdown of each element:

  1. Type: This mandatory field defines the type of change introduced. Common types include:
  • feat: Introduces a new feature.
  • fix: Fixes a bug.
  • refactor: Refactors existing code without changing functionality.
  • docs: Updates documentation only.
  • style: Applies style changes that don’t affect functionality (e.g., formatting).
  • test: Adds missing tests or updates existing ones.
  • build: Updates build tools or configurations.
  • chore: Performs miscellaneous maintenance tasks (e.g., updates dependencies).

2. Scope (Optional): This optional field provides additional context for the change within a specific area of the codebase. It’s enclosed in parentheses after the type.
For example: feat(auth): Implement login functionality

3. Description: This mandatory field offers a concise description of the change introduced. It should be written in the imperative mood (e.g., “fix” instead of “fixed”).

4. Body (Optional): This optional section provides a more detailed explanation of the change, including motivation, reasoning, or breaking changes. Use bullet points for better readability.

5. Footer(s) (Optional): This optional section can include specific information like:

  • BREAKING CHANGE: Indicate backwards-incompatible changes introduced in the commit. Use this sparingly and explain the impact on existing code.
    Example: BREAKING CHANGE: Removed deprecated authentication method
  • Issue references: Reference related issues using keywords like “Fixes” or “Closes” followed by the issue ID (e.g., “Fixes #123”).

Melos Versioning Behavior

Now that we understand what semantic versioning and conventional commits are, we can link it back to how it ties into Melos.
Melos leverages the type of commit introduced in a version to determine the appropriate version bump during the update process:

  • feat: Introduces a new feature and triggers a minor version bump.
  • Other Types (fix, refactor, etc.): Represents bug fixes, refactoring, or other non-feature changes and triggers a patch version bump.
  • ! after Type: If a commit message type includes an exclamation point (!) directly following the type (e.g., feat!), Melos interprets this as a breaking change and triggers a major version bump.

This approach aligns with Semantic Versioning (SemVer) principles, ensuring clear and predictable versioning based on the nature of changes introduced in your package.

Hands-on Example: Bumping Versions in Melos

Let’s put what we have learnt to the test.

Step 1: change directory into the root directory of your mono repo and Initialize git by running the command below.

git init

Step 2: Open the swiftcommerce_ui package in your favorite editor

Step 3: Open the swiftcommerce_ui.dart file in your editor. (the code we write here doesn’t really matter, just trying make changes for git to track)

Step 4: Add a new function to the calculator sample code by pasting the code below.

Int addTwo(int value) => value + 2;

Step 5: Open your terminal and commit the code you’ve written by pasting this command

git commit -am "feat: add addTwo function"

Step 6: Now that we are done with creating a new feature. Paste the command below to generate a new version of your package

melos version

After the command you should see an output like below. Melos gives you a preview of what the new version of the package will be — in this case 0.0.2.
To accept it type y to accept the changes.

The amazing thing about this is that, melos automatically updates your change log with all your commits, bumps up the version in your pubspec.yaml and also creates a tag of your project in git for you.

Step 7: Add a new function to the calculator sample code by pasting the code below.

Int addThree(int value) => value + 3;

Step 8: Open your terminal and commit the code you’ve written by pasting this command

git commit -am "fix: add addThree function”

Step 9: Now that we are done with creating a new patch or fix. Paste the command below to generate a new version of your package

melos version

After the command you should see an output like below. Melos gives you a preview of what the new version of the package will be — in this case 0.0.2+1. Because in our commit we specified that it was a fix, Melos knows to only bump up the patch
To accept it type y to accept the changes.

Step 10: let’s repeat the same process above but this time as a major update

/// add this code 
Int addFour(int value) => value + 4;

/// commit your code using the command below
// git commit -am "feat!: add addFour function”

/// version your package with the command below
// melos version

The new version of the package will now be — 0.0.3

Melos Versioning Behavior (Gotcha!):

You may have noticed from the example above that, minor updates update the patch version.

Melos uses the type of commit introduced to determine version bumps during updates. However, there’s an important distinction for projects with versions below 1.0.0 (considered pre-stable):

Pre-stable (Below 1.0.0):

  • feat: Creates a patch version bump (unlike minor for stable versions).
  • ! after Type: Triggers a minor version bump (instead of major for stable versions).

Stable (1.0.0 and above):

  • feat: Creates a minor version bump.
  • Other Types (fix, refactor, etc.): Creates a patch version bump.
  • ! after Type: Creates a major version bump (as expected).

Key Point: To achieve a major version bump for a pre-stable project, you need to manually update the version to 1.0.0.

Using a local package with Melos

In the previous section, we discussed creating and versioning packages within your Melos workspace. Now, let’s explore how to use these local packages in your applications.

Step 1: Adding the dependency
Open the pubspec.yaml file of your application. In the case of our example, open the pubspec.yaml file of the vendor_app we created above.

Under the dependencies section, add a dependency for your local package like below.

dependencies:
swiftcommerce_ui: 0.3.0

Step 2: Running melos bs

Navigate to the root directory of your Melos workspace in your terminal. Execute this command.

melos bs

This command installs dependencies and links local packages within your workspace, ensuring your application can access the components from your local package.

Step 3: Using the package components

Once melos bs completes successfully, you can import and utilize the components provided by your local package within your application code.

Understanding Pubspec Overrides

When adding local packages to your Melos workspace, you only specify the version in your application’s pubspec.yaml file. However, Melos utilizes a mechanism called pubspec_overrides.yaml to ensure your application can locate and use the local package effectively.

Behind the Scenes:

  1. Automatic Generation: Melos automatically generates a pubspec_overrides.yaml file within the root directory of each application that depends on local packages.
  2. Local Path Reference: This file contains a crucial piece of information: the path to your local package directory. This reference ensures that Flutter knows where to find the package.
  3. Automatic Linking: When you run the melos bs command, Melos automatically links the local package using the path specified in pubspec_overrides.yaml.

Your pubspec_overrides.yaml file should look like the code below

dependency_overrides:
swiftcommerce_ui:
path: ../../packages/swiftcommerce_ui

Publishing Your Package with Melos

Once you’ve developed and versioned your package within your Melos workspace, you can publish it to pub.dev to make it accessible to others. Here’s how Melos streamlines the publishing process:

Prerequisites

  • A pub.dev account: Ensure you have a registered account on pub.dev, the official Dart package repository.
  • Versioning: Your package should have a version number following Semantic Versioning (SemVer) principles. Refer to the previous sections on versioning for details.

Publishing with Melos

  1. Navigate to Workspace Root: Open your terminal and navigate to the root directory of your Melos workspace.
  2. Execute the command below.
# Publish packages with dry run
melos publish

Key Points:

  • By default, melos publish operates in dry-run mode, validating your package structure and dependencies without actually publishing it.
  • To publish your package, use the — no-dry-run flag along with the command: melos publish — no-dry-run.
# Publish packages without dry run
melos publish --no-dry-run
  • Melos leverages the versioning information you’ve defined in your package’s pubspec.yaml file to determine the appropriate publication version.

Conclusion

In this article we discussed how to effectively manage your Flutter mono repository using Melos. From initial setup and workspace configuration to package versioning, local package integration, and publishing, Melos streamlines the development process within a single codebase.

By leveraging Melos’ features, you can ensure efficient dependency management, streamlined local package usage, and clear version control for your Flutter projects.

If you want to explore further, visit the links below

--

--

David Cobbina

Software Engineer & Co-founder at Explorease, crafting high-performance applications that make a real difference in people's lives.