A Practical Engineering Perspective from Building ShenDesk
When integrating an online customer support system into a mobile app, I went through a long and sometimes frustrating process of technical exploration and trade-off analysis.
At the beginning, I was strongly attracted to several “technically ideal” options.
For example:
- Letting app teams fully build their own UI while I only provide APIs, maximizing flexibility
- Or integrating a native SDK to pursue the most “pure” native experience
On paper, these options look elegant.
In practice, as the project evolved, I gradually realized that solutions driven purely by technical ideals often fail to meet the complexity of real-world business scenarios.
This realization didn’t come from theory, but from feedback.
I had in-depth conversations with customers across more than a dozen industries, including e-commerce, education, SaaS, finance, and government-related organizations. Through real production deployments and continuous feedback loops, several critical decision factors became very clear.
Key Constraints That Actually Matter in Production
1. Time-to-launch is the primary bottleneck
Most teams want to launch customer support within one to two weeks, without disrupting their existing app flows.
“Can this be integrated quickly and painlessly?”
This question outweighed almost every other consideration.
2. UI consistency directly impacts user trust
Users expect the support interface to feel like a natural part of the app:
- No jumping outside the app
- No visually fragmented third-party windows
Even subtle inconsistencies in style or interaction can significantly degrade perceived quality and trust.
3. Functional completeness is non-negotiable
Modern customer support is not just about sending text messages.
Users expect:
- Image and file uploads
- AI bot integration
- Queueing and agent transfer
- Satisfaction ratings and feedback
These are no longer “advanced features”; they are baseline expectations.
4. Operational capacity is limited
Most engineering teams are small.
Maintaining a fully custom-built support module—especially real-time communication, state synchronization, and reconnection logic—quickly becomes a long-term operational burden rather than a one-time engineering effort.
5. Multi-platform UI becomes a hidden cost multiplier
If teams are asked to build and maintain their own chat UI across Android, iOS, and H5:
- Development timelines effectively double
- Maintenance complexity compounds over time
What looks like “maximum flexibility” at the beginning often turns into long-term technical debt.
The Strategy That Emerged
Based on these constraints, the technical direction became clear:
For most companies, the optimal path is a solution that is highly controllable, fast to integrate, functionally complete, and reusable across platforms.
Such a solution must provide enough flexibility—style customization, theme configuration, contextual parameters—while also relying on a mature and stable server-side foundation: queueing logic, agent assignment, chat history, notifications, and satisfaction surveys.
This philosophy is what guided the design of ShenDesk.
In the following sections, I’ll compare several common approaches for integrating customer support into mobile apps, and explain why we ultimately committed to WebView-based embedding, including real pitfalls we encountered and concrete implementation details.
SDK Integration or API + Custom UI?
Both SDK integration and API-driven custom UI can make sense in very specific, highly customized scenarios.
However, in real production environments, they often introduce significant hidden costs and risks.
Common Pitfalls of SDK-Based Integration
“Native SDK integration” is frequently marketed as the best possible user experience.
In actual engineering work, however, it often comes with underestimated complexity and long-term maintenance costs.
Below, I’ll break this down from three perspectives: development complexity, performance impact, and maintainability.
1. High integration complexity and strong platform coupling
SDKs typically require separate integration for Android and iOS, along with a non-trivial set of dependencies, permissions, and lifecycle hooks.
A small misconfiguration can easily result in runtime errors or crashes.
A common Android integration example:
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.INTERNET"/>
<application>
...
<activity android:name="com.sdk.chat.ChatActivity"
android:theme="@style/SDKTheme"
android:exported="true"/>
</application>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ChatSDK.initialize(
apiKey = "your_key",
userId = user.id,
context = this
)
}
Typical problems include:
- SDK upgrades changing initialization parameters or permission requirements
- Internal services or broadcasts interfering with host app behavior
- Incomplete ProGuard/R8 configuration causing crashes in release builds
2. Noticeable binary size and performance overhead
Many third-party support SDKs bundle:
- WebSocket clients
- Image caching libraries
- Local databases
- Built-in UI templates, fonts, and animations
This often results in:
- App size increases of 3–5 MB
- Slower startup time, especially on low-end devices
- Dependency conflicts and method count issues
3. Limited UI customization and fragmented UX
Most SDKs expose a closed, self-contained “support UI module.”
val intent = Intent(this, ChatSDK.getChatActivityClass())
startActivity(intent)
You cannot embed it like a Fragment, nor can you control UI details at the DOM or layout level.
This leads to:
- Visual inconsistency with the rest of the app
- Restricted navigation and interaction patterns
- Poor control over localization and theming
4. Opaque updates and difficult debugging
Because SDKs are maintained by third parties, their internal logic is largely invisible.
E/ChatSDK: WebSocket failed to connect.
E/ChatSDK: Internal message parser error.
You cannot step into the source code, inspect state machines, or analyze message queues and retry logic.
In some cases, heavy obfuscation turns the SDK into a complete black box.
5. Weak integration with business context
Suppose you want your support system to understand what product or order the user is currently viewing and route the chat to the appropriate agent group.
With SDK-based integration, this is often extremely difficult:
- Contextual data passing is limited or unsupported
- Real-time business events cannot be injected
- Server-side routing logic is not exposed
ChatSDK.sendMessage("{ \"type\": \"product\", \"id\": \"123456\" }")
Structured messages like this are often rejected or silently reformatted, making advanced product-level features impossible.
The Trap of “API + Custom UI”
At first glance, building your own chat UI using APIs sounds flexible and empowering.
In reality, you quickly discover that a customer support system is not just a chat box, but a high-complexity asynchronous real-time system.
It involves message states, queueing, reconnection, file uploads, agent transfers, and satisfaction feedback—each with its own edge cases.
1. Message synchronization is deceptively complex
A real chat system must handle:
- Sending / sent / failed states
- Read receipts
- Unread counters
- Out-of-order messages
- Deduplication
function sendMessage(content: string) {
const msgId = generateClientMsgId();
renderMessageLocally({ id: msgId, status: 'sending', content });
sendToServer(content, msgId)
.then(() => updateStatus(msgId, 'sent'))
.catch(() => updateStatus(msgId, 'failed'));
}
This logic breaks easily under network interruptions, retries, or mismatched acknowledgements.
2. Reconnection and message recovery are full of edge cases
Ensuring “no message loss” requires far more than reconnecting a WebSocket.
You need:
- Last-message tracking
- Incremental sync APIs
- Deduplication and ordering guarantees
socket.onopen = () => {
const lastTimestamp = getLastMessageTimestamp();
fetch(`/api/messages/since?ts=${lastTimestamp}`).then(syncMessages);
}
Clock drift, latency, and concurrent sends can quickly create inconsistent states.
3. UI state management easily spirals out of control
A chat UI is effectively a finite state machine:
- Before session
- Queueing
- Active conversation
- Finished
Network failures, agent transfers, or server restarts can trigger unexpected state transitions, and every edge case must be handled manually.
4. Critical logic lives on the server, not in the API docs
Agent routing, business hours, evaluation eligibility, rate limits—many behaviors are governed by server-side rules that APIs alone do not fully describe.
Triggering UI actions prematurely often leads to failed or confusing user experiences.
5. File uploads are surprisingly painful
Uploading images or files requires:
- Storage signing (S3, OSS, etc.)
- Validation, progress tracking, retries
- Message linking and expiration handling
What looks simple quickly becomes one of the most error-prone parts of the system.
WebView Embedding: A Lightweight but Powerful Approach
After extensive evaluation, we chose WebView-based embedding as the default integration model for ShenDesk.
In this approach, the app embeds a hosted support page inside a WebView, while preserving UX continuity.
This allows us to achieve:
- Faster integration
- Lower client-side complexity
- Greater customization flexibility
- Stronger system-level control
Key Advantages
Low integration cost
Apps only need to open a WebView and pass basic parameters.
Easy updates
UI changes do not require app updates or store reviews.
Cross-platform consistency
One implementation works across iOS, Android, web, and other channels.
Context-aware integration
User identity and business context can be injected via URL parameters or cookies.
Final Thoughts
In theory, SDKs and fully custom UIs promise control and performance.
In practice, WebView offers a more balanced solution for most teams operating under real-world constraints.
This is not a shortcut—it is a deliberate engineering trade-off.
Wrapping up
ShenDesk is still evolving.
If you’ve ever built or deployed a real-time chat system, I’d genuinely love to hear your experience—
how you handled live updates, load balancing, or flexible deployment models in production.
Let’s compare notes.
If you’re curious
I’ve been building ShenDesk, a customer support chat system designed to run reliably
both online and on your own infrastructure.
- 🌐 Website: https://shendesk.com
- 📘 Documentation: https://docs.shendesk.com
You can try it for free, whether you prefer a hosted setup or self-hosting.
Feedback from developers interested in self-hosted systems, real-time communication,
and customer experience engineering is always welcome.
UI snapshots
Visitor side
Fast loading, no message loss
Agent side
Reliable, feature-rich, built for real-world support work
Web admin panel



Top comments (2)
That was a very long read... But asides that, I think your product is pretty good. I wish you luck with it.
Thanks for reading, and thanks for the kind words.
I took some time to read through your personal site, and honestly, I’m impressed — getting into programming and actually building things at your age is awesome. I wish I had started that early.
Also, fun coincidence that we’re into some of the same games. That’s usually where a lot of interest in computers and software begins.
Appreciate the comment, and good luck with what you’re building.