Designing for the reality you don't control
Three encryption decisions in one stretch of work, each forcing the same choice: design honestly for the reality you have, not the one you wanted.
A lot of product work is about the happy path, the version where everything you assumed is true. The decisions that actually shaped my journaling app this week were the opposite. Each one came from the gap between what I wanted to be true and what was. A bug I had already diagnosed wrong. A key that can disappear. A privacy promise I could only half keep.
The diagnosis I was sure about
I went into a session convinced the encryption flow was breaking because a login token was not surviving a page reload. It was a clean theory. It fit the symptom, which was a sign-in screen that kept asking people to reconnect and never let them through. So I wrote it down and started moving toward the fix.
Then I instrumented the flow and watched it run on a real account, step by step. The token was present the whole time. The actual problem was a missing permission. The app was trying to reach a private storage folder it had never asked the user for access to, and a related setting was switched off entirely. My confident theory was wrong, and the only reason I caught it was that I stopped trusting my own explanation long enough to look at what the code was really doing.
That is the cheapest lesson I keep paying for. The diagnosis you are most sure about is the one to check first, because the confidence is exactly what stops you from checking.
Designing for the key that is gone
Once the flow was unblocked, the harder design question was what to do when the encryption key cannot be found. In my setup, the key lives in a hidden folder in the user's own cloud storage. That is good for privacy, but it means the key is not fully under my control. The user can delete it. The platform can remove it when an app is disconnected. So "the key is missing" is not an edge case to apologize for. It is a state the product has to handle on purpose.
The path I built generates a fresh key, stores it, and moves the user back into the app, no dead ends. But the more important decision was about restraint. My first instinct was to treat any failure to load the key as a reason to start fresh. That is dangerous. If the storage was just briefly unreachable, a network blip or a rate limit, regenerating the key would throw away one that was actually fine.
So I split the failure space. A key that is provably gone, the folder returns nothing, or provably broken, the file is there but unreadable, earns a fresh start. A key that is merely unreachable earns a retry, never a regeneration. Today this distinction costs nothing, because entries are still stored in plain text and the key protects nothing yet. The moment that changes, the distinction is the difference between a recoverable hiccup and quietly destroying someone's data. Build it before you need it.
The privacy I could not keep
The last decision was the one I felt worst about. When users sign in with their Google account, the consent screen asks for more than I want: their name, profile picture, and other public profile fields. For a private journal, that felt like too much, so I tried to remove the profile permission and ask people for a display name inside the app instead.
It did not work, and the reason is worth sitting with. My authentication provider requests that permission at a level I do not control. I removed it from every place I could, signed out, revoked access, and signed back in fresh. The permission was still there. The platform sends it regardless of my configuration.
That left a real choice, and an honest one. I cannot change what the platform requests. I can change what I do with it. So I keep the only field I actually use, the name, and store nothing else. The data footprint is far smaller than the consent screen implies, and the protection that matters, the encryption around journal entries, is untouched by any of this.
It would be tidier to claim I minimized everything. I did not. The useful frame, the one I will reuse, is to separate what you control from what you do not. Minimize the first ruthlessly. Be transparent about the second, including in the privacy policy, rather than pretending the gap is not there. Users can tell the difference between a product that collects little and one that says it does.
Mental models
Verify the confident diagnosis first. Certainty is the signal to check, not the permission to skip checking.
Before any irreversible action, separate "permanently gone" from "temporarily unavailable." Most damage in recovery flows comes from treating the second like the first.
Minimize what you control, and be honest about what you cannot. Privacy is built as much in the disclosure of the gap as in the closing of it.