Every "ATS-friendly resume" article says the same three things: avoid tables, avoid images, use standard fonts. None of them tell you why, or what actually happens when an ATS chokes on your file. So I built a resume tool with a real ATS-friendliness checker baked in, and I want to walk through the actual failure modes and the logic behind detecting them.
The failure modes nobody explains
Most "ATS optimization" advice is folklore repeated without anyone checking if it's still true. Here's what's actually mechanical:
1. Multi-column layouts get read in the wrong order. A two-column resume with a sidebar (photo, skills, contact info on the left; experience on the right) looks great to a human. To a parser that reads left-to-right, top-to-bottom in raw text extraction, your "Senior Engineer, 2022–Present" line can get glued to the middle of a skills list from the sidebar. The content isn't missing — it's just structurally scrambled by the time it reaches a keyword matcher.
2. Photos aren't just "unprofessional in the US" — they're structurally risky. A photo embedded in a sidebar block often forces the parser's layout detection into column-mode, which is when the reordering problem above kicks in. It's not the image itself that's the issue; it's what the image's positioning does to everything around it.
3. Non-standard section headings silently drop content. ATS software is frequently tuned to look for a small set of expected headings — Experience, Education, Skills — and treats anything unrecognized as either noise or a "custom" bucket that doesn't get weighted in keyword scoring. Rename "Experience" to "Where I've Made Impact" and you might be invisible to the exact matching logic that's supposed to find you.
4. Font choice affects text extraction, not just aesthetics. Decorative or unusual fonts can break character-mapping in poorly-implemented PDF text extraction, turning "Manager" into garbage characters. This is rarer with modern parsers but still shows up with legacy ATS installs that a lot of large companies still run.
Building the checker
I didn't want to just tell users "use a plain template" — I wanted the tool to actually look at their resume and their chosen template and flag the specific risk. The logic ended up being pretty small:
function checkATSFriendliness(resume) {
const suggestions = [];
const riskyTemplate = resume.template === "modern" || resume.template === "creative";
if (resume.photo && riskyTemplate) {
suggestions.push({
severity: "warning",
message: "This template places your photo in a sidebar — some ATS parsers misread sidebar content."
});
}
if (!SAFE_FONTS.includes(resume.fontPair)) {
suggestions.push({
severity: "warning",
message: "Unusual font selected — stick to Helvetica or Times for maximum ATS compatibility."
});
}
const missingHeadings = STANDARD_SECTIONS.filter(
type => !resume.sections.some(s => s.type === type && s.visible)
);
if (missingHeadings.length > 0) {
suggestions.push({
severity: "tip",
message: `Consider adding standard section headings (${missingHeadings.join(", ")}).`
});
}
return suggestions;
}
This runs in addition to a separate keyword-match scorer: paste a job description in, and it tokenizes both the JD and your resume content, strips common stop-words, and shows you what percentage of the JD's meaningful vocabulary actually shows up in your resume — plus the specific missing terms, sorted by length.
The part that surprised me
The templates people find "prettiest" (dark sidebar, photo, colorful accents) are almost always the riskiest ones structurally. There's a real tension between what looks good to a human skimming for 7 seconds and what a parser can reliably read. My solution was to just offer both, and be explicit about the tradeoff instead of pretending one template is objectively best.
Try it
I built this into a free resume builder — no signup, everything saves to your browser's local storage, PDF export included. If you want to see the checker in action or just need a resume built: solvebar.com/tools/resume-builder
Happy to answer questions about the tokenization/scoring approach in the comments — there's a lot more nuance in stop-word selection than I expected going in.

Top comments (0)