Engineering Challenges
Some of the most valuable engineering lessons I have learned came from solving problems where the obvious fix was not the correct architectural solution.
The following challenges demonstrate how I investigate complex problems, identify underlying architectural assumptions, evaluate trade-offs, and design solutions that work within real application constraints.
1. MCP Identity and Secure User Context
Project: Investment Portfolio Tracker
Area: MCP, authentication, authorization, LLM tool calling, API architecture
The Challenge
While building my Investment Portfolio Tracker, I exposed portfolio and document capabilities through an MCP server so that external AI clients could interact with the application.
The difficult part was not exposing the tools themselves. The challenge was determining how the backend could reliably identify the current application user when a request originated from an external MCP client.
I also did not want the LLM to know or provide internal database identifiers
such as user_id. Those identifiers are implementation details
of the backend and should not be treated as trusted input from an AI model.
Architectural Reasoning
I initially approached the problem as an authentication problem, but realized that it was more fundamentally an identity propagation and trust-boundary problem.
The external AI client needs a way to authenticate as a particular application user, while the backend should remain responsible for resolving that identity and performing authorization.
This led me to separate external authentication from internal database identity. The AI client does not need to know the application's internal user identifier.
Solution
I introduced Personal Access Tokens (PATs). A user can create a PAT from the application with an expiry time and configure that token in their MCP/LLM client.
When the MCP client makes a request, the backend validates the PAT and resolves it to the corresponding application user. The authenticated identity is then carried internally by the backend while executing the requested tool.
This allows a user to ask an AI client something such as:
Connect to my investment portfolio tracker and tell me my current portfolio.
The LLM can discover and call the appropriate portfolio tool without
needing to know the user's internal user_id. The backend
already knows which authenticated user the request belongs to.
Protecting Internal Resource Identifiers
I encountered a similar problem when exposing user documents through the assistant.
Internally, the database uses a chat_id to identify a
conversation. I did not want this internal database identifier exposed
to the LLM.
Instead, I introduced a public UUID for the chat and exposed it together with useful metadata such as the chat title and summary.
This created another design problem: if the LLM does not know the internal
chat_id, how can it determine which conversation contains
documents relevant to the user's question?
I solved this by providing meaningful metadata and the public UUID to the model. The LLM can reason about the available conversations using their titles and summaries, select the relevant public UUID, and provide it through the tool interface.
The backend then resolves that public identifier to the internal database record and performs the required authorization and database operations.
Engineering Principle
The main principle I took from this challenge was to keep external interfaces separate from internal implementation details.
The LLM should interact with stable, meaningful resource references, while the backend remains responsible for authentication, authorization, identifier resolution, and access to internal database fields.
This challenge taught me to think carefully about what information should cross a system boundary, which component should be trusted with that information, and where identity and authorization decisions should happen.
2. Production File Storage and Attachment Architecture
Project: FamilyShell LLP — Health Records module
Area: Production debugging, Cloudflare R2, MongoDB, file storage, security, data migration
The Challenge
During my internship at FamilyShell LLP, I was working on the Health Records module of a production family management application.
While testing the attachment functionality, I discovered that documents older than a certain period were no longer opening.
Rather than treating this as an isolated frontend or API issue, I traced the complete attachment pipeline to determine where the failure originated.
Investigation
I found that MongoDB was storing presigned URLs for files stored in Cloudflare R2. Because presigned URLs are temporary, storing the URL itself meant that the database was effectively storing an expiring reference to the file.
This was particularly important because the application handles sensitive health-record documents. Simply switching to permanently public URLs would have introduced an unnecessary security concern.
I researched the available Cloudflare R2 access approaches and compared their implications for the application's requirements.
Architectural Decision
I proposed storing the file's object path rather than storing a temporary presigned URL.
When a user requests to view an attachment, the backend can use the stored object path to generate a new short-lived presigned URL.
This provides a better separation of concerns:
- MongoDB stores the persistent identity of the object.
- Cloudflare R2 stores the actual file.
- The backend controls access to the file.
- The generated URL can have a limited lifetime.
I discussed the findings and proposed architecture with the team and received approval to proceed with the change.
Handling Existing Production Data
The architecture could not simply be changed for new uploads because existing production records already contained presigned URLs.
For the old records, I extracted the object path from the existing presigned URL and used that path to generate a new short-lived URL when the user requested to view the attachment.
This allowed the application to move toward the new architecture without breaking access to existing files.
DOC and DOCX Handling
During the same work, I identified another issue. The application supported DOC and DOCX attachments, but the browser could not directly render those formats in the existing viewing flow.
Based on the application's requirements, those file types were changed to a download flow instead of browser rendering.
Preserving Original Filenames
This exposed another issue: downloaded files were using Cloudflare object identifiers as their filenames, which meant the user's original filename was lost.
I changed the MongoDB schema to preserve the original user-provided filename separately from the storage object identifier.
New files could therefore retain the user's filename when viewed or downloaded, while existing records could continue to work using their existing storage identifiers and filenames.
Engineering Principle
This challenge reinforced the importance of tracing a production problem through the complete data flow instead of fixing only the visible symptom.
It also taught me to consider security, existing production data, backward compatibility, and user experience together when changing backend architecture.
3. Timezone-Aware Health Record Reminders
Project: FamilyShell LLP — Health Records module
Area: Domain modeling, timezone handling, FastAPI, MongoDB, scheduling
The Challenge
The Health Records module allows a user to create a health record for themselves, a family member, or even another person.
This means a health record has an important distinction between the creator and the subject.
While investigating the reminder system, I found that reminders were using the current user's local timezone when converting a reminder time to UTC.
The system was also sending the reminder to the creator, even though the reminder could belong to the health-record subject.
Why This Was an Architectural Problem
Consider a user creating a reminder for a health-record subject who lives in another country.
If the user enters 9:00 AM, the intended meaning should be 9:00 AM in the subject's timezone, not 9:00 AM in the creator's timezone.
Using the creator's timezone could therefore cause the reminder to be delivered at the wrong local time for the person the reminder concerns.
This made it clear that the reminder system was using the wrong entity as the source of timezone information.
Research and Solution
I explained these architectural issues in a team discussion and was asked to research common approaches to timezone handling.
Based on that research and the application's domain requirements, I introduced a preferred timezone field for users.
The timezone is associated with the user who is the subject of the health record. Existing users were assigned a default timezone so that the change would remain compatible with existing application data, while users can update their timezone later.
Moving Validation to the Backend
I also moved timezone validation from the Flutter frontend to the FastAPI backend.
The reason is that validation affecting application correctness should have a backend source of truth. Client-side validation can improve the user experience, but the backend should not rely on the client to enforce important data rules.
New Reminder Flow
With the updated architecture, reminder creation follows the subject of the health record.
- The backend identifies the health-record subject.
- The backend retrieves the subject's current preferred timezone.
- The reminder's local day and clock time are interpreted in that timezone.
- The resulting datetime is converted to UTC.
- The UTC value is stored in MongoDB.
- The scheduler uses the stored UTC value to trigger the reminder.
Engineering Principle
The main lesson from this challenge was that correct technical behavior depends on correctly modeling the domain.
The system should not assume that the person performing an action is always the person the action concerns. Once the distinction between creator and subject was made explicit, the correct timezone and reminder behavior became much clearer.
This also reinforced my preference for keeping important validation and business rules on the backend, where they can be applied consistently regardless of which client is using the API.