I started ClipboardX because of a very small annoyance.
While developing and testing Android apps, I was constantly moving things between my phone and my Windows PC:
- logs
- URLs
- error messages
- screenshots
- bits of text I needed on the other device
My workflow was often:
Copy -> Telegram/WhatsApp -> send to myself -> open on PC -> copy again.
Once or twice, that's nothing.
Do it several times a day for months and it becomes surprisingly annoying.
So I had what seemed like a very simple idea:
Copy something on Android and have it available on Windows.
I thought this would be a small side project.
It wasn't.
The original plan
My first mental architecture was roughly:
Android background process
|
v
Backend
|
v
Windows app
When the clipboard changes, send the new value.
Done.
Except Android has very good reasons for not allowing arbitrary applications to sit in the background reading everything a user copies.
And suddenly the central feature of my clipboard app was fighting the operating system.
The keyboard
Eventually I realized I needed a different approach.
Instead of treating ClipboardX only as a background utility, I integrated clipboard functionality into an Android keyboard.
That gave the product a natural place where clipboard access actually makes sense: while the user is typing.
It also created an entirely new problem.
I had never intended to build a keyboard.
A keyboard sounds manageable until you start thinking about:
- layouts
- languages
- predictions
- special keys
- symbols
- input behavior
- keyboard lifecycle
- different screen sizes
- all the tiny interactions people expect from something they use hundreds of times per day
At one point this nearly killed the project.
I was spending more time thinking about how to build a decent keyboard than about the problem I originally wanted to solve.
What eventually got me unstuck also became one of the most useful lessons I learned from this project:
I didn't need to build every layer myself.
I found an existing open-source keyboard foundation that I could legally adapt and build on, then focused my work on the ClipboardX-specific functionality.
My problem wasn't:
How do I build the best Android keyboard?
It was:
How do I make clipboard synchronization useful and reliable?
That distinction saved a huge amount of work.
Then "send the clipboard" stopped being simple too
Once I had Android and Windows talking to each other, the architecture looked deceptively straightforward:
Device A
|
| clipboard update
v
Backend
|
| relay
v
Device B
The first successful transfer was a great moment.
Then I started asking annoying questions.
What if Device B disconnects halfway through?
What if the connection dies immediately after receiving a message?
What if I retry and deliver it twice?
What if Android goes into the background?
What if the destination applies the clipboard value but fails before acknowledging it?
What if the same message appears again after reconnecting?
What if 100 clipboard changes happen quickly?
At that point, "send this string to another device" had turned into a small distributed systems problem.
The system eventually needed concepts like:
- transfer IDs
- acknowledgements
- retries
- deduplication
- reconnect handling
- delivery state
- accounting that doesn't charge twice for the same transfer
One particularly annoying class of bugs came from situations where the clipboard had actually arrived at the destination, but the acknowledgement never made it back.
From the user's perspective:
It worked.
From the backend's perspective:
Did it?
Those are the bugs that made me appreciate observability much more than I did before this project.
I also overengineered it
At one stage I designed a transport "ladder".
The idea was that ClipboardX could try multiple ways of moving data between devices and choose the fastest available path.
I experimented with a transport ladder involving WebRTC/P2P, TURN, QUIC and fallback paths.
On paper, it was much cooler than a simple relay.
In practice, I was building complexity before I had earned the need for it.
Today the production path is intentionally much simpler.
I would rather have one boring transport that works reliably than several clever transports that fail in interesting ways.
I may revisit the more advanced transport architecture later, but only after the basic path is extremely reliable.
That has probably been my biggest architectural lesson from ClipboardX:
Design so complexity can be added later. Don't implement all of that complexity on day one.
Images made it interesting again
Text clipboard synchronization is relatively small.
Then I added images.
Now I wasn't just moving a JSON payload containing a string.
A screenshot can be several megabytes. A photo can be much larger.
That introduces another set of questions:
- payload sizes
- transfer handling
- memory usage
- interrupted transfers
- retries
- performance on weaker devices
- what happens when one side disappears halfway through
Again, the visible feature was tiny:
Copy image here -> image appears there.
The amount of machinery behind that interaction was not.
Getting it working wasn't the same as shipping it
This was probably the bigger lesson for me.
I've built personal projects before where the interesting technical part worked, I proved the idea to myself, and then I gradually stopped working on them.
The first 80% is fun.
The remaining 20% is often:
- weird edge cases
- settings screens
- reconnect bugs
- installers
- billing
- translations
- monitoring
- store requirements
- deployment
- migrations
- privacy policies
- hundreds of tiny UI problems
ClipboardX had the same phase.
There were several points where I genuinely considered dropping it.
But this time I wanted to see what happened if I kept going after the fun prototype stage.
What ClipboardX looks like today
After roughly a year of working on it on and off, ClipboardX is now actually in production.
It currently has:
- an Android application
- an Android keyboard with clipboard access
- a Windows application
- two-way text and image synchronization
- local clipboard history
- QR device pairing
- end-to-end encrypted transfers
- no required user account
- backend/cloud infrastructure behind the device communication
It is available on Google Play and the Microsoft Store.
The funny part is that the product still looks conceptually almost identical to the sentence I started with:
Copy on Android. Paste on Windows.
Most of the work went into making that sentence boring enough that the user doesn't have to think about what happens underneath.
What I'd do differently
If I started again today, I would build the simplest reliable path first.
No transport ladder.
No premature optimization for every possible network topology.
No solving hypothetical scale problems before I had users.
I would still think about extensibility, but there is a big difference between:
This architecture shouldn't prevent us from adding X later.
and:
Let's implement X now because we might need it someday.
ClipboardX taught me that difference the expensive way.
I'd also spend more time testing the boring failure paths earlier.
The happy path is usually easy:
send
-> receive
-> success
The interesting problems live here:
send
-> receive
-> connection dies
-> acknowledgement disappears
-> reconnect
-> retry
-> is this a duplicate?
-> did we already charge for it?
-> what state is the UI supposed to show?
Those scenarios ended up mattering much more than making the first demo look fast.
The main lesson
ClipboardX started as:
I don't want to send things to myself on Telegram anymore.
It turned into my first project where I had to think about the whole path from an idea to something people can actually install and use:
Android, Windows, backend infrastructure, reliability, deployment, billing, stores, monitoring and support.
And I think that is what changed the project for me.
Getting code to work is one problem.
Getting a product to keep working outside your machine is a very different one.
If you're curious, ClipboardX is here:
I'm still developing it myself, and I'm especially curious how other developers would approach the same problems - particularly Android's clipboard restrictions and reliable cross-device delivery.
Would you keep the architecture intentionally relay-based, or eventually bring P2P transports back once the basic path is stable?


Top comments (0)