Storing Resources Wisely: How to Organize Resources in a Multi-Module Project

Doubletapp
11 min readDec 18, 2023

--

Hello, my name is Nikita Chernobrisov, and I develop Android applications at Doubletapp. A year and a half ago, we started working on the “Yandex Travel” application, which is available on the Play Store, and you can read a detailed case study here. At the beginning, we faced many architectural debates, particularly about how to store and use resources. As is customary, our initial solutions turned out to be unsuccessful. I’ll tell you, dear readers, which rake hits left more traces and what conclusions we came to.

Who is this article for?

This article will be useful for those who are starting to work on a multi-module project or have already encountered such problems during development:

  • Any minor change in resources triggers a rebuild of the entire project.
  • You have to constantly go to the team chat and ask if someone has added the icon you need.
  • Resource duplicates, whether it’s a copy in one module or in different ones.
  • It’s difficult to understand if the project has the necessary style since there are already hundreds; it’s easier to add a new one, and no one will notice anyway.
  • You just don’t like how your resources are stored, and you want to improve it :)

Disclaimer! The story will include both optimal solutions and frankly bad ones in the chronological order in which they appeared in our project. Therefore, if you want to borrow the experience, I recommend either reading the article to the end or scrolling down and seeing what we ultimately achieved.

A small design project in Figma and an Android project with a brand book structure were prepared for the article.

Figma link.

Repository with the project link.

Let’s get started

So, the “client/manager/someone assigning us tasks” came to us and said that we need to develop an application with ten different features, and some separate features will be embedded in their other projects in the future. Designers have already drawn layouts for priority features. As conscientious developers, we immediately start thinking about a multi-module architecture so that it will be easier for us and our descendants to work with this machine in the future. We outlined the contract for how the modules will interact with each other, how network requests will be made, and so on. In the end, we came to the question of what we do with our resources — icons, images, colors, texts.

For convenience, we call the “application resource storage device” in the team a “brand book,” and we will also use this term in the article. Now we decide what goals the brand book should achieve:

  • Each module of the application has the ability to use all the resources it needs.
  • Editing the brand book does not cause a long rebuild of most of the project.
  • Each resource is stored in a single instance, and we have no doubt that the resource used in the layout is present in the project.
  • We don’t need to constantly create new styles if designers haven’t gone that route.

Contract with designers

Before we start thinking about a solution, let’s check our layouts. Now we are not looking at whether all the edge cases of feature operation are taken into account but whether all styles and resources are extracted into separate components. It is important that all layouts are built on a certain set of styles and components — such constraints will improve the design because it will be strictly regulated by a set of elements and rules for their construction.

So, here are two screenshots: the first is a flawed example of working with Figma, the second one is the work of a designer.

A flawed example of working with Figma: the text is inserted without being tied to a style, and we see all its parameters such as size, color, and so on.
Designer’s Figma: a style extracted into components is applied to the text. What matters to us is not every parameter, but the style’s name, and this also applies to color.

In the first version, we see that the person did not think about what types of text we will have in our application and drew by eye. Such an approach will lead to the loss of consistency in the design of text in different parts of the application, and we as developers will have to constantly check each text parameter during layout. We don’t want this, so we ask the designer to put Figma in order so that it looks like the second screenshot: beauty, we see the names of styles and colors, and when laying out, we set them because they will already be in our project (about where and how to put them, read on).

This rule works not only for texts but also for colors, icons, and illustrations — ask the designer to do such work. And then the next stage of preparations in Figma begins.

Design system (suitable for further auto-export)

Now our Figma is systematized, and the chance that new styles will appear unexpectedly without our knowledge has been reduced. We can start adding resources to the project, but the prospect of reviewing each new screen for not added common components sounds unpleasant; we would like to collect everything in one place. This is what we will ask our designers to do — a separate page in Figma with a description of all components and styles. It should look something like this:

How a minimal design system looks in Figma

The “colors” frame includes a list of all colors (by the way, some color values may duplicate, and that’s normal: the design team will be able to change the color of a group of elements in the future without affecting others), used in the design. “Typography” includes text styles, “illustrations” contains complex images that we cannot convert from SVG to vector drawable, “icons” are icons whose vectors can be perfectly converted by the studio, and “components” includes more complex components, such as buttons or elements that will be presented as custom views. Other interface descriptions, such as animation speed, colors, gradient angles, and so on, can also be listed here.

With this, preparations in Figma are complete, and we can finally go to the studio to assemble our brand book in the project.

The first option that should not be repeated

As mentioned earlier, brand book elements should be available to all modules that work with interface rendering. Of course, we come to the point that the brand book will be a separate module, and it will be imported into other modules in the future. We add colors, icons, illustrations in different sizes to the project, and create custom views for buttons. For convenience in the future comparison of colors in Figma and in the project, it is worth keeping the namings that designers gave — this way, you don’t have to remember each time under which name a particular icon is stored. I would recommend only adding a unique prefix or suffix to the name for the project. This will save us from the situation where we need to connect a separate feature to another project, but there is already a resource with the same name (in any project, there will be an icon with the name “ic_arrow” or a color named “background”). Here is what the declaration of application colors will look like:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color
name="fifty_two_challenge_app_text_primary">#000000</color>
<color
name="fifty_two_challenge_app_text_inverse">#FFFFFF</color>
<color
name="fifty_two_challenge_app_text_control_secondary">#747474</color>
<color
name="fifty_two_challenge_app_control">#71DE9D</color>
<color
name="fifty_two_challenge_app_background_main">#D6DED9</color>
<color
name="fifty_two_challenge_app_background_secondary">#FFFFFF</color>
<color
name="fifty_two_challenge_app_control_pressed">#4FC982</color>
<color
name="fifty_two_challenge_app_control_disabled">#AFAFAF</color>
<color
name="fifty_two_challenge_app_control_highlighted">#F3F3F1</color>
</resources>

This method of storing resources is low-cost in terms of implementation time and will work fine in the early stages of the project. If there is a hundred percent certainty that the project will never grow beyond 3–4 modules, then you can stop here. But I can confidently say that someone from the developers will hang themselves when you have 100+ modules because the project build will take time comparable to the duration of a flight to Alpha Centauri. Let’s figure out why. Let’s draw a dependency diagram for a small project consisting of 6 modules:

As we can see, the brand book is connected to each feature, and the build system works so that when a module is changed, not only its build is triggered, but also the build of all modules that depend on it. As a result, we get a situation where changing the color of a button triggers a chain of rebuilds almost throughout the entire project. Although painting buttons is our main task, we are not ready to wait that long, so let’s think further.

Breaking down our monolith

Our brand book turned out to be too monolithic. The solution will be to split it into several, but how? During our suffering with the monolith, we came to quite logical and elementary conclusions, although not obvious at the beginning of development.

  • Firstly, constants such as colors or icons change extremely rarely, and each of the screens uses them.
  • Secondly, more complex custom views change much more often. These can be entire forms for filling out with various conditions for displaying different fields. Not all screens, however, require each added view from the brand book.

Based on this, we make a simple conclusion about splitting the brand book: leave the most in-demand and rarely changing elements in the current module, and move complex views into their own modules. As a result, we will get a dependency diagram like this:

This way, the number of rebuilds will be much less. Yes, when adding a new icon, modules of all features will still be rebuilt, but this will not happen with every ticket. But in the case of, for example, changing the color of text in our custom toolbar, only one feature will be rebuilt. This brand book structure will remain, and we will move on to automating the export of resources from Figma.

Figma auto export

Let’s add one more tuning element to the brand book — automatic resource export. For this, we will use the FigmaExport utility. We will briefly go through the algorithm of connecting it to our project. It is worth clarifying right away: the utility uses FigmaApi, which requires moving the downloaded components to libraries. The export functionality is paid, so your team must have a paid subscription.

Preparation in Figma

We didn’t just ask designer colleagues to create separate components for colors, texts, and images and place them on dedicated frames for nothing. This is a necessary condition for FigmaExport to work. If this work has been done, there is one last step — publishing components to the team library, a shared repository of styles, to provide external access to them. To do this, right-click on each of the frames and select “Publish selected components.” Done, let’s go to the studio!

Connecting FigmaExport to the project

Let’s structure this part as a step-by-step guide:

1. Download the latest version of the source code from here.

2. Extract the contents of the archive and place it directly into our core/brandbook module next to the src directory. The module structure will look like this:

3. Optionally, you can delete the figma-export_XcodeExport.bundle folder; it won’t be needed.

4. In the screenshot, you can notice the figma-export.yaml file — this is the configuration for running the export. We need to create it ourselves. You can find a ready example file here, and a full list of parameters here. Let’s go through the essential parameters in the file:

  • figma-export.yaml
figma:
lightFileId: 75cKsK1wB2obpck3TfevLb
common:
colors:
nameValidateRegexp: '^([a-zA-Z_]+)1_color'
typography:
nameValidateRegexp: '^([a-zA-Z_]+)1_style'
images:
nameValidateRegexp: '^([a-zA-Z_]+)1'
icons:
nameValidateRegexp: '^([a-zA-Z_]+)1'
android:
mainRes: './src/main/res'
icons:
figmaFrameName: Icons
output: "icons"
images:
figmaFrameName: Illustrations
format: webp
output: "images"
scales: [1, 1.5, 2, 3, 4]
webpOptions:
encoding: lossy
quality: 90

lightFileId — the page ID in Figma, which you can get from the URL. Open Figma, copy the text after “file” and before the project name. If the application supports a dark theme, use darkFileId and do the same.

nameValidateRegexp — a regular expression validating the resource name. If there is a mismatch with the regular expression in the name, you will be notified in the terminal.

nameReplaceRegexp — as mentioned above, we want to add a prefix to resource names in the project, and here we use this parameter. Specify fifty_two_challenge_app_$1_color, and “text_primary” will turn into “fifty_two_challenge_app_text_primary_color”.

In the android section, specify where the directory for storing resources mainRes is located. Then, for icons and illustrations, specify the output directories where to save them relative to mainRes. Additionally, for illustrations, set parameters such as format (export format, e.g., webp) and required sizes of scales that can be 1 (mdpi), 1.5 (hdpi), 2 (xhdpi), 3 (xxhdpi), 4 (xxxhdpi).

5. Add your Figma token to allow FigmaExport access to designs. Go to Figma profile settings, find the “Personal access tokens” section, copy and add it to environment variables using the command:

export FIGMA_PERSONAL_TOKEN=[place_for_your_token]

6. Now, let’s go! To run the export, execute the following commands in the terminal, and the resources from Figma will be imported into the project:

# Export icons
./figma-export/Release/figma-export icons -i figma-export.yaml

# Export images
./figma-export/Release/figma-export images -i figma-export.yaml

# Export text styles
./figma-export/Release/figma-export typography -i figma-export.yaml

# Export colors
./figma-export/Release/figma-export colors -i figma-export.yaml

In conclusion

The method of storing resources is not often discussed when creating the architecture of a multi-module application, but its incorrect choice can lead to significant problems both technically and in the convenience of creating new screens. After the work was done, we significantly accelerated the project build time and obtained a convenient structuring of all application resources. From the drawbacks, I would highlight only that now, for each individual view, you have to spend time creating an additional module, but it will not compare with the time spent on rebuilds.

Based on the adventures of our team, I would highlight the following points:

  • Always ensure that the module with main resources does not change too often since it triggers a rebuild of a significant part of the project.
  • If some component changes too often but is not used throughout the whole application, consider moving it to a separate module.
  • If you don’t have auto-export, discuss in the team who, how, and when adds new resources, how you will name resources, and where you will store them. Seeing a component in Figma, a developer should not spend 10 minutes trying to figure out if it already exists in the project.
  • A convenient Figma is the responsibility not only of the designer; everyone, suggest your improvements — colleagues will appreciate it.

--

--