Modules: Organize the Jungle Codebase

In this lesson you'll learn to

  • βœ“Split code into modules, control access, and use using { } imports.

Split code into modules, control access, and use using { } imports.

πŸ“– Reference & full walkthrough: Modules: Organize the Jungle Codebase

🧩 Your capstone piece: Module layout of the whole rally project


Modules: Organizing and Sharing Verse Code

A growing UEFN project is dozens β€” sometimes hundreds β€” of .verse files. Without a way to group them and control what's visible across them, every name would collide and every file would see everything. Verse's answer is the module: a named container for related code. Modules are how you split a project into systems (SaveSystem, NpcSystem, UISystem), how you pull in Fortnite's APIs with using, and how you decide what's <public> (shared) versus <internal> (private to the module). This lesson ties the whole track together: the access specifiers you'll set here use the same angle-bracket syntax as the effect labels from earlier lessons.

Two kinds of module you already use

<!-- section-art:two-kinds-of-module-you-already-use --> Modules: Organizing and Sharing Verse Code: Two kinds of module you already use

Dual Module Paths

Every using line at the top of a Verse file names a module. You've been using modules since your first device:

using { /Fortnite.com/Devices }                 # Fortnite's device APIs
using { /Verse.org/Simulation }                  # agent, @editable, core sim types
using { /UnrealEngine.com/Temporary/Diagnostics } # Print and logging

Those slash-paths are Epic's modules: /Fortnite.com/..., /Verse.org/..., /UnrealEngine.com/.... A using opens a module so you can write its names unqualified β€” button_device instead of the full path. These three are the most common in real code (/Verse.org/Simulation and /Fortnite.com/Devices appear in the vast majority of device files).

The second kind is your own modules β€” the systems you build. In our project, a file opens sibling systems the same way:

# Real corpus using-lines for project modules.
using { Verse.Scripts.Utils }
using { SaveSystem }
using { ConfigSystem }

So using is a single idea applied to both Epic's library and your own code: bring this module's public names into scope.

How a module is structured

A module is declared with := module. There are two forms.

Folder modules β€” a folder in your Verse project is a module, named after the folder. Our project's permissions.verse declares the whole tree this way. This is real, lightly trimmed corpus code:

# Real shape from permissions.verse β€” folders declared as modules.
Scripts<public> := module:
    SaveSystem<public> := module:
        Core<public> := module:
    NpcSystem<public> := module:
    UISystem<public> := module:
    Utils<public> := module:
    Unused<internal> := module:    # visible only inside Scripts

Indentation shows nesting: Core lives inside SaveSystem, which lives inside Scripts. The full path to Core is Scripts.SaveSystem.Core.

Inline modules β€” you can also declare a small module right in a file with braces:

# An inline module grouping math helpers in one named box.
MathHelpers<public> := module {}

Either way, the module is a namespace: a box that holds names (functions, classes, constants) and keeps them from colliding with names in other boxes.

Accessing module contents: using vs. qualified names

Once a module exists, there are two ways to reach what's inside.

1. Open it with using β€” then write its names bare:

using { Verse.Scripts.Utils }   # open the Utils module

# now Format (a public function inside Utils) is available unqualified
Label := Format("Score", Points)

2. Qualify the name β€” write the path inline without a using. The digest does this constantly to disambiguate:

# Fully-qualified call β€” no using needed, path written inline.
Color := (/Verse.org/Colors:)MakeColorFromHex("FF8800")

Use using for modules you touch a lot (cleaner), and qualified names when you reference something once or need to disambiguate two same-named things from different modules.

Scoping: <public> vs. <internal>

This is the part that trips people up, and it's the reason modules are more than just folders. Each name in a module carries an access specifier β€” the same angle-bracket syntax as effect labels β€” that controls who can see it:

  • <public> β€” visible to code outside the module. This is the module's published surface.
  • <internal> β€” visible only within the same module (and its submodules). A private helper.
  • <protected> / <private> β€” finer-grained, mainly for class members (private = only this class; protected = this class and subclasses).
# A module that exposes one function and hides its helper.
ScoreUtils<public> := module:

    # Published β€” other systems may call this.
    FormatScore<public>(Points : int) : string =
        "Score: {PadNumber(Points)}"

    # Internal β€” only ScoreUtils itself can use this. Outsiders can't see it.
    PadNumber<internal>(N : int) : string =
        if (N < 10) then "00{N}" else "{N}"

From another system, ScoreUtils.FormatScore(...) works after using { ScoreUtils }, but PadNumber is invisible β€” it's an implementation detail. This is the same instinct as keeping classes focused: expose the smallest surface that does the job, and hide the rest. Our permissions.verse uses exactly this β€” most systems are <public> so games can use them, while Unused<internal> stays hidden.

The default matters: if you don't write an access specifier, a member is more restricted than <public> β€” so to share something across modules you must explicitly mark it <public>. Forgetting that is the #1 module mistake (covered below).

A worked example: a small shared utility module

Putting it together β€” a focused Utils module with a public helper, opened from a device:

# --- file: Scripts/Utils/string_utils.verse ---
# (lives in the Scripts.Utils folder-module)

# Public: any system may use this.
Repeat<public>(Text : string, Times : int) : string =
    var Result : string = ""
    for (I := 0..Times - 1):
        set Result += Text
    Result
# --- file: Games/MyGame/banner_device.verse ---
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

banner_device := class(creative_device):

    OnBegin<override>()<suspends> : void =
        Line := "===================="
        Print(Line)```

One module owns the helper; the device opens it with `using` and calls it unqualified. If `Repeat` weren't marked `<public>`, the device couldn't see it β€” the compiler would say the name is unknown.

## Naming and layout conventions

Our project organizes each system as a folder-module with a consistent file pattern (from the project conventions): `_module.verse` (shared functions), `_data.verse` (data structures), `_config.verse` (configuration), `_controller.verse` (the `creative_device`). A system is **self-contained**: it exposes a public surface and keeps its internals private. Following a predictable layout means anyone β€” human or AI helper β€” can find the right file by name.

## The pitfalls β€” what trips people up

<!-- section-art:the-pitfalls-what-trips-people-up -->
![Modules: Organizing and Sharing Verse Code: The pitfalls β€” what trips people up](/static/lesson-sections/verse-modules--the-pitfalls-what-trips-people-up.png)

*Module Pitfalls*


**1. Forgetting `<public>`.** A function or class with no access specifier is **not** automatically visible across modules. You `using` the module, the name still isn't found, and it's baffling β€” until you realize you never marked it `<public>`. To share, you must say so.

**2. Confusing `using` with importing the file.** `using` opens a *module* (a namespace), not a file. The folder structure defines the module path; `using { Verse.Scripts.Utils }` opens the `Utils` folder-module, regardless of how many files are in it.

**3. Wrong module path.** `using { Utils }` vs `using { Verse.Scripts.Utils }` β€” the path must match the actual nesting. If `Utils` is nested under `Scripts`, you generally need the qualified path (or a `using` of the parent). A path that doesn't match the folder tree won't resolve.

**4. Over-exposing internals.** Marking every helper `<public>` turns your module into a tangle that other systems start depending on, so you can't change it later. Publish the minimum; keep helpers `<internal>`. This is single-responsibility at the module level.

**5. Circular `using`.** Two modules that each `using` the other create a dependency cycle. Keep dependencies flowing one direction (e.g. games depend on systems, not the reverse), as the project's layered structure does.

**6. Treating Epic paths and project paths as different mechanisms.** They're the same: `/Fortnite.com/Devices` and `Verse.Scripts.Utils` are both module paths opened by `using`. Epic's just start with a slash (absolute, from the engine root).

## Recap

- A **module** is a named container (namespace) for related code; `:= module` declares one, as a **folder module** or an **inline** `module {}`.
- **`using`** opens a module so you can write its names unqualified β€” the same idea for Epic's APIs (`/Fortnite.com/Devices`) and your own systems (`Verse.Scripts.Utils`). You can also write **qualified names** inline.
- **`<public>`** publishes a name across modules; **`<internal>`** keeps it private to the module. No specifier means *not* public β€” you must explicitly mark what you share.
- Expose the **smallest public surface** and hide the rest; lay systems out predictably (`_module`/`_data`/`_config`/`_controller`).
- Watch for the classics: forgetting `<public>`, wrong module paths, over-exposing internals, and circular dependencies.

## References

- [Verse Language Reference (Epic)](https://dev.epicgames.com/documentation/en-us/uefn/verse-language-reference)
- [Modules and paths in Verse (Epic)](https://dev.epicgames.com/documentation/en-us/uefn/modules-and-paths-in-verse)
- [Access specifiers in Verse (Epic)](https://dev.epicgames.com/documentation/en-us/uefn/access-specifiers-in-verse)
- Grounded in the Verse Cortex knowledge base: real corpus `permissions.verse` (folder-module tree with `<public>`/`<internal>`), corpus `using` lines (`/Fortnite.com/Devices`, `/Verse.org/Simulation`, `Verse.Scripts.Utils`, `SaveSystem`), the digest's qualified-name calls, and project conventions (CityOfBrains module layout: `_module`/`_data`/`_config`/`_controller`).
πŸ”’

Keep going β€” free

You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.

Sign in free to continue this lesson
Pass the quiz above to chart this quest in your Journal.

Sources

/guides/verse-modules
Source

Biloxi Studios original lesson

Guided course

Add this lesson to your free study plan.

🧭 The Keeper’s log

Quest complete? Chart your next heading from the 🐻 North Jungle expedition.

β›΅ Take me there