RFC-98 Follow up Forge Dynamic Modules

RFCs are a way for Atlassian to share what we’re working on with our valued developer community.

It’s a document for building shared understanding of a topic. It expresses a technical solution, but can also communicate how it should be built or even document standards. The most important aspect of an RFC is that a written specification facilitates feedback and drives consensus. It is not a tool for approving or committing to ideas, but more so a collaborative practice to shape an idea and to find serious flaws early.

Please respect our community guidelines: keep it welcoming and safe by commenting on the idea not the people (especially the author); keep it tidy by keeping on topic; empower the community by keeping comments constructive. Thanks!

Project Summary

Following our recently published RFC-95 we would like to provide further details on our thinking for dynamic modules on Forge. This RFC contains more details on the implementation of dynamic modules and what developers can expect. With this RFC iteration we are also ensuring that using dynamic module will not impair an app’s Runs on Atlassian eligibility.

  • Publish: Jul 3, 2025
  • Discuss: Jul 15, 2025
  • Resolve: Jul 21, 2025

Problem

Dynamic Modules

Recap RFC-95: dynamic modules and triggers provide numerous benefits such as on-demand feature delivery, reduced app size, and enhanced user experience. By loading only the essential modules at runtime, developers can easily introduce new features or tailor the app to various environments without necessitating app updates.

Users enjoy the flexibility of dynamic modules as it allows them to flexibly customize their Atlassian experience. For instance, a task management app may allow users to activate or deactivate modules for features like calendar integrations or notification preferences based on their workflows. Each installation of an app can use different dynamic module configurations making it a popular feature.

Example Jira trigger module with Jira expression filtering:

A user configured for a work item to be auto assigned to the user based on the work item’s priority. This should work for any work item in project “xxx” (dynamically specified by the user). The app dynamically registers a new trigger listening for ‘avi:jira:created:issue’ and ‘avi:jira:updated:issue’ with expression based filter to receive events only for “xxx” project. As a result the app will populate the assignee field with the user’s name based on selected priority.

Proposed Solution update

Dynamic modules are modules that are added to a specific installation during an app’s runtime. Instead of defining all modules in the manifest file, these modules would be added during app execution. Following this principle we did propose a set of APIs to register, update and delete dynamic modules.

However, as we were looking more into more dynamic app module use cases, we learned that some of them sometimes handle user generated content (UGC). This means we need to ensure data residency compliance so that apps using dynamic modules can still be eligible for Runs on Atlassian - unless the app is using remote storage which will make it ineligible for Runs on Atlassian. We therefore had to revise our previously proposed solution approach.

Manifest Changes

We are proposing a new module type dynamicModule for the app manifest. Note that adding dynamicModule does not trigger a major version update as no elevation of privileges is happening.

Developers will

  • define Forge modules that can be created and managed dynamically by the app
  • define a handler to be used for fetching dynamic modules by Atlassian systems. This will need to be a lambda function.

Example Manifest

Example implementation of get-dynamic-modules handler using KVS

  • Apps will provide a mechanism for customers to create dynamic modules from within the app
  • When a new dynamic module is created Atlassian will cache the response for invocations for dynamicModule . If the invocation is for dynamic module and data is present in cache, then data is returned from the cache.
  • Atlassian services will discover dynamic modules and subsequently invoke developer defined handlers.

Creating and managing dynamic modules

Persisted key requirements for dynamic modules:

  • get a list of available dynamic modules
  • register for module types of interest
  • update registered modules
  • delete registered modules

When customers or internal app processes decide they want to create dynamic modules, the app will perform validations and persist dynamic modules within its storage.

An example using KVS can look like

Accessing Dynamic Modules

  • When an extension discovery process executes within Atlassian systems, it first locates the dynamicModule handler defined in the manifest and then invokes it.
  • Upon receiving dynamic modules from the handler, Atlassian systems will perform server-side validations.
  • This data will then be cached, ensuring that for any subsequent discovery, the invocation process is not re-executed if the data is already present in the cache.

Constraints

  1. For key collisions between manifest and dynamic modules, manifest defined keys will get preference i.e. dynamic modules will be ignored.
  2. For collisions between dynamic modules, all such modules will be ignored when processing handler response. For example: if 2 dynamic modules for an installation have a clash, both dynamic modules will be ignored and not available to Atlassian systems.
  3. We are proposing a limit of 100 dynamic modules per app installation. This has been reviewed based on previous RFC feedback. If the number of dynamic modules returned in the handler response exceeds 100, all dynamic modules for that installation will be ignored.
  4. Only those modules that are specified in manifest under use property will be discovered. e.g. if app defines triggers as supported dynamic modules but then returns Jira custom fields as well, custom field dynamic modules will be ignored.

We recommend that apps perform client side validations before modifying dynamic modules. This could typically include things like schema validation, payload sizes, complexity of expressions.

Delivery and Improvements

Following RFC feedback we will continue to plan for an iterative delivery starting with trigger modules for Jira. As we progress, we will gradually expand our offerings to additional module types, covering Confluence as well.

We are evaluating the introduction of app notifications about validation failure, however these will not be provided immediately and we are recommending that apps perform client side validations as mentioned above.

Asks

While we would appreciate any reactions you have to this RFC, we’re especially interested in learning more about:

  1. Are there any major concerns with this new solution approach?
  2. Is there anything critical you believe we are missing, or which should be reconsidered in the above proposal?

Can you share how this helps to reduce app sizes?

1 Like

Hi @BenRomberg sorry I’m not clear on your question. Could you pls provide more details?

Hey Julia,

increasing the limits to 100 per app (per tenant) is definitely a welcome change.

I assume the shape of elements returned by the get-dynamic-modules handler is basically going to mirror the manifest structure i.e.

{
   trigger: [
       {
           key: 'myDynamicTrigger',
           function: 'foobar',
           events: [...],
           filter: {
                expression: 'issue.project == "XYZ"',
                ignoreSelf: true
           }
       }
   ]
}

If that is the case, this will be mostly equivalent to Connect dynamic modules, which looks good to me.

Of course one annoying detail – not inherently part of this RFC – is the fact that if Forge storage is not persisted after uninstalling and reinstalling the app (which sometimes is a troubleshooting step that customers do), we’d also lose the dynamic modules for that instance.

3 Likes

Thanks for the RFC! I also welcome the improvement of having the 100-module limit per app installation and not per site (i.e. over all apps). That way an app can interact independant of other apps :+1:

Two points:

Firstly, I’m not quite understanding the sample code for creating dynamic modules:

Specifically:

  • validateDynamicModule is imported from @forge/storage. Does that imply that the Forge platform will validate, rather than the app? From the text I thought it’s the app’s responsibility to validate.
  • triggerPayload is the module’s parameter definition? The word “payload” is often used for invocations (i.e. runtime), and not for definitions. That got me confused.
  • ${key} is not defined. I guess it should be an argument of createDynamicTrigger, yes?

Secondly, what’s the rationale behind this part?

2 Likes

Thanks for the updated RFC @JuliaDaehne!

If I understand correctly, the new proposal means that we won’t have any APIs to create/update/delete dynamic modules, but just return the currently valid dynamic modules by the dynamicModule function, correct?

How long will the dynamicModule function invocation be cached? What happens if we add a new dynamic module to the app’s KVS? How long, until the function will be called again to make the new module available to the users? Or is there some way we can trigger cache invalidation?

2 Likes

Hi Julia,

Thank you for the improved proposal and the limit adjustment. It is now in line with former behaviour that applies to dynamic registration of webhooks (for Connect apps).

Focusing on this particular use case, it is not clear to me whether it will be possible to register dynamic webhooks on behalf of the app (without explicit admin interaction/confirmation) and then process them using user impersonation (if the Jira event was triggered by some user action), i.e. calling product APIs with asUser().
I see that typical use cases of dynamic modules rather include user-specific features, where it is expected and fair to administer them manually via the UI. However, this approach seems to block generic features, whose main purpose would be performance-optimisation. For example, we would use app configuration data (complex notification rules) to activate and fine-tune dynamic product webhooks instead of relying on some other “dynamic modules-specific” configuration page in Jira (or even in the app), where it is hard to ensure consistent settings from the business requirements perspective (our notification scope rules might be hard to map to the module scopes). In addition to that, customer/admin-based registration is problematic, as our app’s performance may depend on our customers’ competence.

Considering this, do you plan to provide coverage for such use cases?

Best,
Márton

1 Like

I think you meant me not Ben.

Your post states:

Recap RFC-95: dynamic modules and triggersprovide numerous benefits such as on-demand feature delivery, reduced app size, and enhanced user experience. By loading only the essential modules at runtime, developers can easily introduce new features or tailor the app to various environments without necessitating app updates.

I just want to understand how this RFC reduces app sizes.

1 Like

@PaoloCampanelli that’s an interesting call out - I looked into this ‘uninstall/ reinstall’ behaviour the other day for different reasons. It seems that we get anything between 10 and 100 a day across all app installations which is not a lot. But still, I agree that this way of troubleshooting can be a cause a headache - not just from a dynamic module perspective.

Yes we had to change our approach quite drastically from new API to the introduction of this new Forge function. We are still working on the caching implementation and the EAP phase will help us make cache invalidations more robust so we keep cache misses to a minimum.

Sorry for wrong tag @BorisBerenberg ! The intro of this RFC was a recap of the original RFC-95 to give new readers context. With the new proposal we are still addressing the key value proposition of dynamic modules, ie on-demand feature delivery. The app size reduction is an additional benefit but not achieved through this new approach. Is this something you were particularly keen to achieve?

Hi @marton.kelemen asApp use case is supported with dynamic modules.
Any user on a tenancy that has a dynamic module using app installed can configure dynamic modules for the app

Hi @JuliaDaehne

I have questions similar to Ben above:

How often is the extension discovery process run? For example, what if I want to add a dynamic module from my app, right now? Or what if I want to remove a dynamic module from my app, right now?

The new RFC suggests to me that Atlassian’s systems will only be able to poll the app for changes. Although there are many reasons why Atlassian should cache and not poll too often (cost to vendors, system load for Atlassian), there are also plenty of use cases where an app may want to activate a dynamic module with only a short delay. With Connect, for example, this can be done pretty much immediately.

Could there be some sort of API call where a Forge app can request that Atlassian poll the endpoint immediately? This could even be made a requirement of implementation (meaning that Atlassian caches the previous results until this API is accessed to trigger a refresh).

For diagnostic purposes, it would also be very helpful if there could be an API where vendors could validate the results after having applied previously-supplied dynamic modules. As articulated in the RFC, there are a number of edge cases that will cause dynamic module installation to fail or be ignored (duplicated keys, wrong module types, too many modules, and presumably other issues with unexpected response format or missing functions).

Given this, I believe Atlassian should provide a method for validating the dynamic modules (either before or after installation). While vendors can attempt to validate a portion of the payload themselves, some of the data required to validate is only accessible to Atlassian (and in any case, this would save every vendor from having to reinvent the same wheel).

2 Likes

Hi @scott.dudley - please see this reply from Julia.

We will be exploring how to make the solution more robust from a caching perspective post EAP and see how we can make this a more seamless process.

For validation, we will provide certain validators (e.g. in sdk) to ensure apps can verify structure of these modules etc. However, since we don’t own the underlying storage, we recommend apps validate counts/collisions etc themselves before persisting this data. We will be exploring mechanisms to notify apps if validations fail on Atlassian side post EAP as well.

Hi @SapanGupta and @JuliaDaehne

Thanks for the clarification. I think the response from Julia answers a different question though. The gist of the thread above seems to focus on minimizing cache misses (ie. not calling the vendor app too often), but that is the exact opposite of the problem I am referring to: how to ensure that the module list can be updated in a timely manner? If the answer to the cache invalidation question is truly “we don’t know”, then I would suggest that this be considered in-scope for this RFC as part of the answer to this question:

  1. Is there anything critical you believe we are missing, or which should be reconsidered in the above proposal?

I suggest that Atlassian’s SLA for applying any dynamic module changes be a maximum of 60 seconds from when the app wants to make a change. For comparison, in Connect, module changes are applied almost instantly. Even a five-minute delay is too long (users who are expecting to be able to see certain features will no longer be able to do so quickly).

I also do not believe that it is reasonable to poll the app’s function every 5 minutes either, given that this could generate costs for the vendor in terms of compute and potentially KVS (so it is true that vendors also care about cache misses).

In short, I do not see any possible way to address both of these issues simultaneously with the new method.

The API-based approach first suggested in RFC-95 seems far superior from a vendor perspective, because it addresses all of these problems simply and without significant cost, and it also allows for immediate validation feedback too.

I definitely appreciate the shift to allowing 100 dynamic modules per app rather than per tenant, but the function-based approach here feels like a step backwards. Is there a specific problem that Atlassian is hoping to address by moving to this approach?

6 Likes

Hi @scott.dudley

Thanks a lot for your feedback, we really appreciate it. We’re doing a proof of concept around the approach proposed in this RFC at the moment and will provide an update once we have more details on cache TTL and invalidation strategies on our side.