How I Discovered a Major Flaw in My Resume Management System
Today, I discovered a major flaw in my resume management system.
Initially, I thought the problem was straightforward.
Students upload resumes.
Those resumes are associated with their job applications.
The university can control how many resumes a student is allowed to keep.
Simple.
At least, that's what I thought.
Then I started thinking about what happens when students actually use the system.
And that's where the architecture started to break down.
The Resume Deletion Problem
Suppose a university allows students to upload only a limited number of resumes.
Eventually, a student reaches that limit.
To upload a new resume, they may need to delete an older one.
Now imagine that the older resume was already used while applying for a job.
What happens to that application?
If the application depends directly on the student's current resume, deleting the resume can create a serious problem.
The company could end up seeing an application containing only basic information such as:
- Student name.
- Phone number.
- Email address.
The resume—the document that may contain the candidate's skills, projects, education, and experience—is suddenly gone.
That's not acceptable.
And this exposed a deeper problem in my design.
A historical application should not depend on the current state of a student's resume.
My First Attempt: Cascade Delete
My initial relationship used cascade deletion.
Conceptually:
Student
↓
Resume
↓
Application
If the resume was deleted, anything depending on that relationship could also be deleted.
That immediately raised a red flag.
A student deleting an old resume should never cause a previously submitted job application to disappear.
An application represents a historical action.
Deleting a piece of currently managed data shouldn't erase that history.
So I needed a different approach.
Changing Cascade Delete to SET NULL
The immediate solution was to change the relationship behaviour to SET NULL.
Now the student could delete the resume without deleting the application.
That was certainly safer.
But it created another problem.
The application could now contain:
Resume = NULL
The application still existed.
But the company reviewing it no longer had access to the resume that was associated with the application.
So I had solved the database deletion problem without solving the actual product problem.
And that led me to a better question:
Should an application depend on the student's current resume at all?
The answer was no.
Applications Need Historical Data
A job application represents a point in time.
When a student applies for a job, the application should represent what the student submitted at that moment.
That means the application shouldn't continuously depend on mutable data that the student can change later.
This is the same architectural principle I had already started applying to other parts of my Placement Portal.
Historical records should preserve the information that was relevant when the event occurred.
For resumes, this meant introducing the concept of a snapshot.
The Resume Snapshot Approach
Instead of making the application depend on the student's current resume, I can preserve the relevant resume information at the time of application.
The flow becomes:
Student Resume
↓
Apply for Job
↓
Extract Resume Content
↓
Application Snapshot
↓
Historical Application
Now the original resume can change without affecting the application.
The student can:
- Upload another resume.
- Edit their resume.
- Delete an old resume.
The previously submitted application remains intact.
This is an important distinction.
The student's resume represents current data.
The application snapshot represents historical data.
But Then I Discovered Another Problem
Once I decided that the application needed resume information rather than simply a reference to the uploaded file, another question appeared.
How do I extract useful information from a resume?
At first, "resume parsing" sounds easy.
Read the PDF.
Extract the text.
Done.
But resumes are not standardized documents.
Different students can structure their resumes completely differently.
Some may have:
- Skills.
- Projects.
- Education.
- Certifications.
- Experience.
- Social links.
Others may use tables, columns, different layouts, or completely different section structures.
There is no guarantee that two resumes will look anything alike.
That makes resume parsing its own engineering problem.
Starting With Simple PDF Text Extraction
I didn't want to immediately build an intelligent resume parser.
So I deliberately started with something much simpler.
The current parser has two primary responsibilities:
- Extract text from PDF resumes.
- Store the extracted text for further processing.
That's it.
No AI.
No OCR.
No complicated document understanding.
Just basic PDF text extraction.
This gives me a reliable starting point without introducing unnecessary complexity before I actually need it.
Giving Students Control Over Parsed Content
There is another problem with automatic parsing.
Even if a parser extracts text successfully, that doesn't mean it understands the resume perfectly.
Formatting can be lost.
Information can be interpreted incorrectly.
Sections can become difficult to distinguish.
So instead of assuming that the parser is always correct, I decided to keep the student involved in the process.
After uploading a resume:
- The system extracts the text.
- The extracted content is shown to the student.
- The student reviews it.
- The student can edit the content.
- The finalized information can then be used by the application.
This changes the role of the parser.
It isn't trying to replace the student.
It is helping the student get their information into the system faster.
The student remains the final authority over what is submitted.
The New Resume Management Flow
With these changes, the overall workflow becomes:
Upload Resume
↓
PDF Text Extraction
↓
Parsed Content
↓
Student Reviews
↓
Student Edits
↓
Finalized Resume Data
↓
Apply for a Job
↓
Application Snapshot
↓
Historical Application
The important part is what happens after the application is submitted.
The application no longer needs to depend on the student's current resume.
Instead, it preserves the relevant information from the time of application.
Why This Architecture Is Safer
Consider the original scenario again.
A student has:
Resume A
Resume B
Resume C
They use Resume A to apply for a job.
Later, they delete Resume A because they need space for another resume.
With a direct relationship, the application could lose access to the resume.
With a snapshot approach:
Resume A
↓
Job Application
↓
Resume Snapshot
Deleting Resume A doesn't destroy the information already captured by the application.
The current resume management system can evolve independently from historical applications.
That's a much stronger architectural boundary.
The Resume Parser Should Eventually Be Independent
Another conclusion I reached while working through this problem is that resume parsing shouldn't become tightly coupled to the Placement Portal itself.
The Placement Portal has its own responsibilities:
- Users.
- Jobs.
- Applications.
- Placement workflows.
Resume parsing has a different responsibility:
Transform an uploaded resume into usable information.
These are related, but they are not the same problem.
That makes the parser a good candidate for eventually becoming an independent service.
Conceptually:
Placement Portal
│
│ Resume
▼
Resume Parser Service
│
│ Parsed Data
▼
Placement Portal
The Placement Portal doesn't need to know how the parser works internally.
It only needs to provide the resume and consume the resulting information.
Future Resume Parsing Improvements
The current implementation is intentionally simple.
There are many things that could eventually be added.
For example:
- OCR support.
- Structured resume extraction.
- Skills detection.
- Education extraction.
- Experience extraction.
- AI-assisted parsing.
- ATS-friendly formatting.
- Support for additional document formats.
But these are future possibilities.
I don't want to introduce them simply because they sound interesting.
The current goal is to establish a reliable foundation first.
Once the architecture is separated properly, the parsing technology can evolve independently.
The Bigger System Design Lesson
The interesting part of this entire problem is that none of these architectural decisions started with:
"Let's build a resume parser."
It started with a much smaller question:
"What happens if a student deletes a resume after applying for a job?"
That edge case exposed a deeper architectural flaw.
The application was depending on data that could change or disappear.
That led to snapshots.
Snapshots created the need for resume content.
Resume content created the need for parsing.
Parsing introduced a new processing responsibility.
And that eventually led to the idea of a dedicated resume parsing service.
The architecture evolved because the edge case exposed the real problem.
Final Thoughts
One of the most valuable things I'm learning while building this Placement Portal is that architectural problems often reveal themselves through edge cases.
I didn't discover this problem while designing the happy path.
I discovered it by asking what happens when a student deletes something.
That single question forced me to rethink the relationship between:
- Resumes.
- Applications.
- Historical data.
- Resume parsing.
- Future processing services.
The final direction is much stronger than simply storing uploaded PDF files.
The resume can change.
The student can change it.
The student can even delete it.
But a submitted application should preserve what mattered when the application was created.
Sometimes the best architectural improvements come from asking, "What happens if the user does something I didn't expect?"
That's where real system design begins.
— Abhishek Roka