Over past some time, our team have been deeply involved in building and maintaining a Server-Driven UI (SDUI) platform — a Kotlin Multiplatform (KMP) based platform that transforms JSON configurations into native Android and iOS interfaces. This journey has been both exhilarating and challenging, filled with moments of “this is brilliant!” and “why is this so complicated?”
In this article, I’ll share our experiences — the victories, the struggles, and the hard-won lessons from creating the platform and working with SDUI. Whether you’re considering building SDUI platform, adopting it, already working with it, or just curious about this architectural approach, I hope our story provides valuable insights.
What is SDUI?
Before diving into the good and not-so-good parts, let me briefly explain what SDUI is. Server-Driven UI is an architectural pattern where the backend defines UI screens as JSON configurations (this is specific to our implementation, you might want to store your screen definitions in another way), and the client SDK dynamically renders these configurations into native UI components. Think of it as having your UI defined remotely, allowing you to update screens, change layouts, and modify user flows without releasing new app versions.
Our platform supports 40+ component types, handles 30+ action types, implements sophisticated caching strategies, and provides enterprise-grade security — all while maintaining a shared codebase between Android and iOS through KMP.

The Good Parts
1. Rapid Iteration Without App Releases
This is SDUI’s crown jewel. The ability to push UI changes, fix bugs, and A/B test different layouts without going through app store approval processes is transformative. We’ve seen product teams iterate on user flows multiple times in a single day — something that would have taken weeks in a traditional native app development cycle.
Real Impact: During a critical launch, we discovered a flow issue in one of the crucial flows. Instead of waiting for the next release cycle (which could take 2–3 weeks), we fixed it server-side and pushed the update within hours. Users experienced the fix the same day.
2. True Multi-Platform Consistency
By leveraging Kotlin Multiplatform (KMP), we’ve achieved something remarkable: a single codebase for business logic, data management, caching strategies, and network handling that works seamlessly on both Android and iOS. The platform-specific layer is minimal — mostly just UI rendering with some complex parsing.
The Architecture: Our SDK follows a four-layer architecture:
- KMP Data Layer: Network, caching, storage (shared)
- KMP Domain Layer: Business logic, repositories (shared)
- Android/iOS SDK Layer: Android/iOS -specific ViewModels and state management
- Android/iOS UI Layer: Jetpack Compose and SwiftUI components
This separation means bug fixes and feature additions benefit both platforms simultaneously, dramatically reducing maintenance overhead.
3. Intelligent Three-Tier Caching
Performance is critical in mobile apps, and SDUI’s caching strategy has been a game-changer. We implemented a three-tier caching system:
- Memory Cache: Lightning-fast access for frequently used screens
- Disk Storage: Persistent storage for offline access and faster subsequent loads
- Network Fetch: Fresh data when needed
The beauty is in the automatic fallback mechanism. If a screen isn’t in memory, check disk. If not on disk, fetch from network. Each tier stores the data for future use, creating a self-optimizing system that gets faster with usage.
Performance Gains: We’ve seen screen load times drop from 800ms on first load to under 50ms on subsequent visits, even with complex screens containing multiple sections and components.
4. Action-Driven Architecture
Instead of tightly coupling UI components to specific business logic, we built an action-driven system where components’ trigger actions are defined in JSON. This creates incredible flexibility — the same button component can navigate, make API calls, show dialogs, or update state, all based on configuration. And the best part? It can be modified and deployed with as simple as a small change in JSON. So, you do a JSON change, and a new flow is ready for users.
Action Types We Support:
- Navigation (screen transitions, native app navigation)
- Data operations (set, cache, global data management)
- UI interactions (alerts, toasts, dialogs, pickers)
- Network requests (with parallel request support)
- System integrations (location, camera, biometrics, file sharing)
- and many more..
The composability of actions — where actions can chain success and error handlers — allows for complex user flows to be defined declaratively in JSON.
5. Comprehensive Performance Monitoring
We built performance tracking directly into the SDK, tracking metrics at every stage:
- Navigation timing
- Cache hit/miss rates
- API request durations
- UI composition times
- Screen render completion
This visibility has been invaluable for identifying bottlenecks and optimizing the platform. We can see exactly where time is spent — whether it’s network latency, cache misses, or UI rendering complexity.
6. Enterprise Security Features
Security was a non-negotiable requirement, and we’ve implemented multiple layers:
- Secure Storage: Encrypted key-value storage using platform key stores
- Storage Encryption: Optional encryption for cached data
- SSL Pinning: Certificate pinning for network security
- Data Protection: In-memory and transmission protection
These features ensure compliance with GDPR, HIPAA, PCI DSS, and SOC 2 requirements, making the platform suitable for enterprise applications handling sensitive data.
7. Rich Component Library
With 40+ component types, we can build complex, production-ready screens entirely through JSON configuration. From basic components like text, buttons, and inputs to advanced ones like maps, camera views, QR scanners, calendars, and file pickers — the platform covers most use cases without requiring custom native code.
8. Flexibility to add new modules
The platform supports adding new native modules within an SDUI environment. This was probably the most satisfying thing and this helps teams to move gradually to SDUI — one module at a time, instead of converting a whole app into SDUI and making it ready for release.
The Hard Parts Nobody (very few) Talks About
1. State Management Complexity
Managing state in SDUI is inherently complex. You’re dealing with multiple state layers:
- Local State: Form data, component-specific state
- Global State: Application-wide data shared across screens
- Cache State: Persisted data that survives app restarts
- API State: Response data from network calls
- UI State: Dialogs, progress bars, loading indicators
The challenge is ensuring these states stay synchronized, especially when actions trigger cascading updates. We’ve spent significant time debugging race conditions where UI renders before state updates complete, leading to stale data or incorrect UI states.
In one example, we encountered a race condition with language changes. When switching from English to Arabic, the RTL layout would apply correctly, but strings would remain in English because the UI rendered before the Arabic strings were fetched from server and finished loading in memory. The fix required careful sequencing of async operations and proper callback chaining.

2. Debugging Challenges
Debugging SDUI applications is fundamentally different from traditional native apps. When something goes wrong, you’re not debugging code — you’re debugging JSON configurations, state transitions, and action chains. Traditional debugging tools provide limited value.
Challenges We Faced:
- No Code to Step Through: Errors manifest in JSON parsing or state management, not in your code
- State Inspection: Understanding the current state across multiple layers requires custom tooling
- Action Tracing: Following action chains through JSON configurations is tedious
- Network Issues: Distinguishing between network problems, caching issues, and configuration errors
We’ve built custom debugging tools and extensive logging, but it’s still more challenging than debugging traditional apps. The learning curve for new developers joining the team is steeper because they need to understand both the SDK architecture and the JSON configuration format.
3. JSON Configuration Complexity
As screens become more complex, JSON configurations grow exponentially. A moderately complex screen can have hundreds of lines of JSON, with nested structures, conditional logic, and action chains. This creates several problems:
- Readability: Large JSON files are hard to read and understand
- Maintainability: Making changes requires careful navigation of nested structures
- Validation: Ensuring JSON is valid and follows the schema is error-prone
- Version Control: Merge conflicts in JSON files are common and difficult to resolve
We’ve seen JSON files grow to thousands of lines for complex screens, making them nearly impossible to maintain without specialized tooling. We are now building a web portal to provide drag and drop support to build the JSON screens on the fly.
4. Performance Overhead
While caching helps significantly, SDUI introduces inherent performance overhead:
- JSON Parsing: Every screen load requires parsing potentially large JSON structures
- Component Resolution: Determining which component to render for each JSON element
- Style Application: Merging global, component, and dynamic styles
- State Reconciliation: Ensuring state consistency across multiple layers
For simple screens, this overhead is negligible. But for complex screens with many components, nested structures, and multiple sections, the overhead becomes noticeable, especially on lower-end devices.
We’ve implemented lazy loading, view recycling, and intelligent caching, but the fundamental footprint remains and we are improving every day. This is a trade-off we accept for the flexibility SDUI provides.
5. Testing Challenges
Testing SDUI applications is more complex than testing traditional apps:
- Unit Testing: Testing individual components requires mocking JSON configurations and state
- Integration Testing: Testing action chains and state transitions requires complex setup
- UI Testing: Screens are dynamic, making it harder to write stable UI tests
- Backend Dependency: Tests often require backend services or extensive mocking
We’ve developed testing utilities and patterns, but the testing experience is still more cumbersome than traditional app testing. The dynamic nature of SDUI means tests are more brittle and require more maintenance. Our research is ongoing on this section.
6. Learning Curve
SDUI requires understanding multiple concepts simultaneously:
- The SDK architecture and how components interact
- JSON configuration format and schema. This is, by all means, a new programming language.
- Action system and how actions chain
- State management across multiple layers
- Caching strategies and when data is fetched vs. cached
- Platform-specific considerations (Android vs. iOS)
New developers typically need 2–3 months to become productive, compared to 2–3 weeks for traditional native development. This is a significant investment, and the learning curve can be overwhelming.
7. Limited Type Safety
JSON is inherently untyped, which means:
- No Compile-Time Validation: Errors are discovered at runtime
- No IDE Support: Limited autocomplete and refactoring capabilities
- Refactoring Risks: Changing component names or structures requires manual updates across all JSON files
- Documentation Dependency: Developers must rely on documentation and examples
We’ve created JSON schemas and validation tools, but they don’t provide the same safety net as strongly-typed languages. This leads to more runtime errors and requires more careful code reviews.
8. Backend Dependency
SDUI creates a strong dependency on backend services. If the backend is down, slow, or returns invalid configurations, the app experience degrades significantly. We’ve implemented offline support and caching, but there are limits to what can be cached.
During a backend outage, users couldn’t access new screens or see updated content, even though the app itself was functioning. This dependency requires robust backend infrastructure and careful error handling.
9. Version Compatibility
As the SDK evolves, we need to maintain backward compatibility with existing JSON configurations. This creates constraints on how we can improve the platform. Breaking changes require coordinating updates across backend services and client apps, which can be a lengthy process.
We’ve implemented versioning strategies, but managing compatibility across versions adds complexity to the codebase and limits our ability to make architectural improvements.
10. Debugging Production Issues
When issues occur in production, debugging is particularly challenging:
- No Direct Access: We can’t directly inspect the JSON or state on user devices
- Reproduction Difficulty: Issues might be configuration-specific or state-dependent
- Limited Logging: Extensive logging impacts performance, so we must be selective
- User Context: Understanding what the user was doing when an issue occurred requires additional instrumentation
We’ve built analytics, performance recording, and error reporting, but debugging production issues still requires more effort than traditional apps. A comprehensive review is required with backend log and our own crash details to understand what would have gone wrong.
Lessons Learned
What We’d (perhaps) Do Differently
- Invest in Tooling Earlier: Custom debugging tools, JSON designer, JSON validators, and configuration generators would have saved significant time. It’s not these were not planned, but we wanted to defer them till the core concept was established. Starting with these early would save a lot of time down the line.
- Stronger Type Safety: We’d implement more validation layers and potentially consider alternatives to pure JSON (like a DSL or typed configuration format).
- Better Documentation: Comprehensive documentation with examples would have reduced the learning curve and prevented many mistakes.
- Testing Infrastructure: Building testing utilities and patterns from the start would have improved code quality and developer confidence.
- Performance Monitoring: Implementing performance tracking earlier would have helped identify bottlenecks before they became problems.
What Worked Well
- Layered Architecture: The separation between KMM and platform-specific layers has been invaluable for maintenance and testing.
- Action System: The action-driven architecture provides the flexibility we needed while keeping the system composable.
- Caching Strategy: The three-tier caching system has been crucial for performance and user experience.
- Security First: Building security features from the start ensured we could meet enterprise requirements without major refactoring.
- Performance Tracking: Comprehensive metrics have been essential for optimization and troubleshooting.
When to Use SDUI
SDUI is not a silver bullet. It’s a powerful tool that makes sense in specific scenarios:
Good Fit:
- Apps requiring frequent UI updates
- Multi-platform applications (Android + iOS)
- Enterprise applications with complex requirements
- Apps with A/B testing needs
- Applications where backend teams need UI control
Not a Great Fit:
- Simple apps with static UIs
- Apps requiring pixel-perfect, highly customized designs
- Projects with limited backend resources
- Teams without the capacity to invest in learning SDUI
Conclusion
Working with SDUI has been a journey of trade-offs. We’ve gained incredible flexibility and rapid iteration capabilities, but at the cost of increased complexity, debugging challenges, and a steeper learning curve.
The platform has enabled us to build features faster, maintain consistency across platforms, and respond quickly to user feedback. But it’s also required significant investment in tooling, documentation, and developer education.
If you’re considering SDUI, weigh the benefits against the costs. For our use case — enterprise mobile applications requiring frequent updates and multi-platform support — the benefits have outweighed the challenges. But this might not be true for every project.
The key is understanding what you’re signing up for: SDUI is powerful, but it’s not simple. It requires investment in infrastructure, tooling, and team education. If you’re willing to make that investment, SDUI can be transformative. If not, traditional native development might be a better fit.
As we continue to evolve the platform, we’re constantly learning and improving. The challenges we’ve faced have made us better engineers, and the solutions we’ve built have made the platform more robust. That, perhaps, is the greatest value of this journey — not just the platform we’ve built (which is invaluable), but the expertise we’ve gained along the way.
Have you built SDUI or similar server-driven architectures? I’d love to hear about your experiences and the challenges you’ve faced. Let’s continue the conversation in the comments.



