| name | remote-dev |
| description | Guidelines for structuring and developing IntelliJ remote development modules. Use when working on Remote Development features. |
Remote Development
Basics
- Structure modules using proper suffixes:
.frontend - UI code that runs on client side
.backend - computation code that runs on server side
.shared - code common to both frontend and backend
- Use RPC for frontend-backend communication:
- Create interfaces with
@Rpc annotation in shared module
- Implement interfaces on backend side
- Use
RemoteApiProvider to register implementations
- All RPC methods must be
suspend functions
- All parameters and return values must be
@Serializable
- When working with backend objects on frontend:
- Use ID serialization (e.g.,
VirtualFile.rpcId() and VirtualFileId.virtualFile())
- For state synchronization, use
StateFlow and Flow
- Don't access on frontend:
- PSI, indexes, file system (directly), VCS
- Debugger, build systems, modules
- Any backend-only services and APIs
Documentation
Detailed documentation in docs/IntelliJ-Platform/4_man/Remote-Development/:
External: Remote Development Overview (user documentation)
Creating Remote Development Modules
After creating any new remote-dev .iml, register it with the project-file helper instead of editing modules.xml by hand:
bun build/jps-module.mjs register <path-to-iml> --fix-iml-eof
The helper updates .idea/modules.xml, also updates community/.idea/modules.xml for community modules, and uses the canonical order by .iml basename.
Module Types and Naming Conventions
-
Standard Module: intellij.<pluginGroup>.<frameworkName>
Example: intellij.java.dsm, intellij.cidr.debugger
-
Remote Development Modules (split architecture):
- Shared Module:
intellij.<feature>
- RPC Module:
intellij.<feature>.rpc (for RPC interfaces)
- Backend Module:
intellij.<feature>.backend
- Frontend Module:
intellij.<feature>.frontend
- Split Modules:
intellij.<feature>.frontend.split, intellij.<feature>.backend.split
For Platform Modules
- Shared Module:
- If working with existing module: Keep as is
- For new module: Create in
platform/feature/ following V2 plugin format
- RPC Module (if needed):
- Create in
platform/feature-rpc/ for RPC interfaces
- Extract all
@Rpc interfaces, DTOs, and supporting classes
- Add dependencies on required serialization libraries
- This module should have no implementation logic
- Backend Module:
- Create in
platform/feature/backend/
- Include
intellij.platform.feature.backend.xml in resources
- Add dependency on
intellij.platform.backend
- Add to
essential-modules.xml
- Frontend Module:
- Create in
platform/feature/frontend/
- Include
intellij.platform.feature.frontend.xml in resources
- Add dependency on
intellij.platform.frontend
- Add to
essential-modules.xml and intellij.platform.frontend.main.iml
- Add to JetBrains Client's product-modules.xml
- Frontend Split Module (if needed):
- Create in
remote-dev/feature/frontend.split
- Configure as V2 plugin module
- Add to product-modules.xml and JetBrainsClientPlugin.xml
For Plugins in Monorepo
- Shared Module:
- Keep shared code in main plugin module
- Register in appropriate product-modules.xml for bundling
- RPC Module:
- Create with
.rpc suffix for RPC interfaces
- Contains only RPC interfaces, DTOs, and supporting classes
- Register in plugin.xml
- Both frontend and backend modules depend on this
- Backend Module:
- Create with
.backend suffix as V2 plugin module
- Add dependency on
intellij.platform.backend and .rpc module
- Register in plugin.xml
- Frontend Module:
- Create with
.frontend suffix as V2 plugin module
- Add dependency on
intellij.platform.frontend and .rpc module
- Register in plugin.xml
- Frontend Split Module:
- Create with
.frontend.split suffix in appropriate location
- Add dependency on
intellij.platform.frontend.split
- Register in plugin.xml
Reactive Programming Patterns for Remote Development
Key Patterns for Reactive State in Remote Development
The following patterns are essential when implementing reactive UI components for Remote Development:
- Backend StateFlow with Initial Value Transfer:
- Use StateFlow for all mutable state on the backend
- Add initial values in DTOs to avoid blocking calls on the frontend
- Use
.toRpc() extension to convert StateFlow to RpcFlow for RPC transfer
- Converting RpcFlow to StateFlow on Frontend:
- Use
toFlow().stateIn() pattern to convert RpcFlow to StateFlow
- Always provide initial values from DTO to ensure immediate UI feedback
- Use SharingStarted.Eagerly for UI components that need immediate updates
-
Example: Reactive Breakpoint DTO
@Serializable
data class XBreakpointDto(
val displayText: String,
val iconId: IconId,
val sourcePosition: XSourcePositionDto?,
val initialEnabled: Boolean,
val initialSuspendPolicy: SuspendPolicy,
val enabledState: RpcFlow<Boolean>,
val suspendPolicyState: RpcFlow<SuspendPolicy>,
)
-
Example: Frontend Component with Reactive State
class FrontendXBreakpointProxy(
private val project: Project,
private val cs: CoroutineScope,
private val dto: XBreakpointDto
) {
val enabled: StateFlow<Boolean> = dto.enabledState.toFlow()
.stateIn(cs, SharingStarted.Eagerly, dto.initialEnabled)
fun isEnabled(): Boolean = enabled.value
}