_blogs
// blogs / 20260814.md
// blogs / 20260814.md
Dev Log: August 14 Wrap-up
Overview
Today was a bit of a marathon across three different languages and as many different environments. I spent the morning polishing my personal expense tracker and the rest of the day untangling some tricky session and IP-forwarding logic in our backend decorators. It felt like one of those days where you're constantly context-switching, but everything somehow clicked into place by EOD.
What I Worked On
Polishing the Expense Tracker UX
I finally got around to cleaning up the "Pending Review" flow in my tracker. Before, if a transaction was flagged from an SMS, it would just sit in the queue even after you opened the notification. I added a small fix to mark transactions as acknowledged the second the category prompt activity opens. It’s a small quality-of-life change, but it makes the app feel way more responsive.
I also went down a bit of a rabbit hole with Compose navigation transitions and an AMOLED black theme. Adding those smooth horizontal slides between screens makes the whole experience feel less like a utility tool and more like a finished product.
The IP Address Rabbit Hole
I ran into a weird issue where our lead-creation logic was losing the visitor's real IP address. Because the requests go through several layers, the final service was seeing the Load Balancer's IP instead of the user's. I had to update our public flow decorator to explicitly forward the client_ip inside the extra data payload.
While I was in there, I noticed a bug in one of our CRM decorators where it was overwriting the IP even if we already had a valid one. Fixed that with a simple check:
if (Util.isEmpty(data.getIpAddress())) {
String resolvedIp = fetchUserIP();
if (resolvedIp != null) {
data.setIpAddress(resolvedIp);
}
}
Handling "Duplicate" Successes
On the frontend side, I had to handle a specific edge case in the pricing form. The backend occasionally throws a DUPLICATE_ENTRY_UPDATED error. Technically, it’s an error code -1, but for the user, it means their data was successfully merged or updated. I updated the component logic to treat that specific message as a success so we don't scare users away with an error toast when everything actually worked fine.
Better Security Errors for the Chat Widget
This was the most satisfying fix of the day. We had a security check that was returning a 200 OK with an error message embedded in the body. The problem? The chat widget would see that 200 OK and literally print the security error as a bot reply to the user.
I changed that to a proper 403 Forbidden exception in the Python service. Then, on the Java side, I had to ensure our custom decorators weren't swallowing that 403.
It’s always a trade-off between generic error handling and preserving enough detail to actually fix things. Using a custom application exception to bubble up the real status code while logging the specifics was definitely the right call here.
Wrapping Up
All in all, a solid day. The backend feels a lot more robust now that we aren't leaking session errors into the UI, and my personal project finally has that "dark mode" polish I've wanted for weeks. Tomorrow, I think I'll dive deeper into some of the remaining reporting logic. Catch you then.