When a Codebase Needs a Map

Joining a new team usually means getting familiar with an unfamiliar codebase. The Slack iOS team’s monorepo, however, was not just unfamiliar—it was genuinely difficult to navigate. With roughly 13,000 files, 27 top-level directories, a mix of Objective-C and Swift, and around 40 developers working in the same repository, the team had accumulated three separate source directories. New hires faced a guessing game every time they needed to add a file: which directory, which subfolder, whose convention?

This was not a new problem. The mess was the result of several half-hearted organization attempts, a missing architecture pattern, and years of rapid team growth. Some developers had simply gotten used to the chaos. But a group of iOS engineers decided the status quo was costing too much time and clarity, so they set out with three goals:

  • Make it quick and obvious where to add a new file, for anyone from a new hire to a veteran
  • Organize directories so they reflect the team’s development pattern
  • Use tooling to keep the hierarchy clean and maintainable going forward

The work came in two phases. First, the larger structural moves: aligning top-level directories for targets, extensions, and frameworks. That phase was relatively painless, taking only a few weeks with a small group of developers. It taught the team a few lessons that carried into the harder second phase: make large moves during off-peak hours, merge master frequently to avoid conflicts, and move files in a way that gets people comfortable with the new layout early.

The top-level cleanup paid off. A side-by-side view from September 2018 shows a much more coherent structure with a clear place for every major directory. The second, more challenging phase was tackling source file organization inside those directories.

Choosing a Folder Philosophy

Consolidating the three source directories into a single App/Source location required consensus on an organizing principle. Research on folder organization is surprisingly sparse for such a common problem, though Uber’s approach to their iOS monorepo offered some useful groundwork on chunking code into modules.

The team considered three structural options: feature-based organization, topic-based (or architecture-based) organization, and ontology-based organization that groups files by relationships. The team landed on a hybrid: feature directories at the top level, with topic-based (specifically MVVM+C) organization inside each feature folder.

/FeatureFolder
   /Coordinators
   /Models
   /Tests
      /Functional
      /Mocks
      /Unit
   /ViewModels
   /Views

The actual migration was tedious. Merge conflicts were a recurring annoyance, remembering to capture every file in a feature namespace was harder than expected, and many files did not fit neatly into the initial folder rules. A few engineers stepped up and performed large coordinated moves to dismantle the old directories and consolidate everything into App/Source.

By January 2020, the file hierarchy looked dramatically different. To keep it that way, the team added a danger rule to prevent developers from adding files back into the old, now-removed source directories. Danger, a tool integrated into the CI system, runs automated checks after commits and posts warnings and errors directly on pull requests, acting as a first line of defense for the new structure.

has_slack_directory_additions = !git.added_files.grep(/Slack/).empty?
has_slackcocoasdk_directory_additions = !git.added_files.grep(/SlackCocoaSDK/).empty?
has_ios_directory_additions = !git.added_files.grep(/iOS/).empty?
if has_slack_directory_additions || has_slackcocoasdk_directory_additions || has_ios_directory_additions
   fail(‘This PR is introducing new files into directories that are
   closed for adding new files. Please add files to App/Source using
   the new convention found in <ahref=”…Adding-a-file-to-Slack-  
   iOS…”>Adding a file to Slack iOS’, sticky: false)
end

House Rules for Folder Hygiene

Even after the big migration, the team continues to refine the organization inside App/Source. A set of house rules guides daily upkeep:

  1. No spaces in folder names—tools like Bazel do not handle them well
  2. No vague “dumping ground” folders like Helper or Utility
  3. Co-locate tests with source code to improve discoverability
  4. Keep files and folders alphabetically sorted
  5. Ensure files live under the correct top-level directory for their target

has_app_test_directory_additions = !git.added_files.grep(/Test/).empty?
if has_app_test_directory_additions
   fail(‘This PR is introducing new files into directories that are
   closed for adding new files. Please add co-locate tests using the
   new convention found in <a href="”link-to-our-housekeeping-">iOS Folder Housekeeping Checklist</a> or feel free to ask in 
   #ios-testing-folder-structure’, sticky: false)
end

The co-located tests rule required tooling enforcement, so the team added another Danger check. Any new pull request that attempts to drop files into App/Tests instead of the relevant feature directory gets flagged automatically.

Beyond tooling, a “folder committee” was formed, with a dedicated Slack channel where developers can ask where a file belongs. Even small moves can generate strong opinions or confusion about placement, having a core group to answer questions makes a difference.

Manual Work First, Tools Second

Tools like SwiftLint, Danger, and custom scripts are essential for long-term enforcement, but they only become effective after the initial cleanup is mostly done. That groundwork often involves a lot of unglamorous manual labor. The team found that a committee with a clear mandate, willingness to work across all parts of the codebase, and a steady stream of tooling to enforce decisions were the keys to progress.

It is an ongoing effort, not a one-time fix. But the payoff is tangible: developers spend less time hunting for files or debating directory conventions, and more time writing code. Understanding the architecture pattern is easier when the structure reflects it, and tooling can eventually keep the codebase clean without requiring constant human attention. For any team with a monorepo and a shared desire for a more pleasant codebase, investing in folder hygiene is worth the effort.