<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: risola_me</title>
    <description>The latest articles on DEV Community by risola_me (@risola_me_a79eac9d2622b19).</description>
    <link>https://dev.to/risola_me_a79eac9d2622b19</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3970495%2F252040fa-6b70-4283-b28a-f5f5d54224ad.jpg</url>
      <title>DEV Community: risola_me</title>
      <link>https://dev.to/risola_me_a79eac9d2622b19</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/risola_me_a79eac9d2622b19"/>
    <language>en</language>
    <item>
      <title>My LLM Kept Making Stuff Up on Resumes. Here’s How I Shut It Down.</title>
      <dc:creator>risola_me</dc:creator>
      <pubDate>Mon, 27 Jul 2026 20:36:50 +0000</pubDate>
      <link>https://dev.to/risola_me_a79eac9d2622b19/my-llm-kept-making-stuff-up-on-resumes-heres-how-i-shut-it-down-15d2</link>
      <guid>https://dev.to/risola_me_a79eac9d2622b19/my-llm-kept-making-stuff-up-on-resumes-heres-how-i-shut-it-down-15d2</guid>
      <description>&lt;p&gt;I'm building NextStep — an AI thing that scores your resume against a job and rewrites it to fit. Two features, both completely dependent on the model not lying. And GPT-4o loves to lie.&lt;/p&gt;

&lt;p&gt;Two bugs made me stop trusting it:&lt;/p&gt;

&lt;p&gt;Score the same resume twice → 87, then 79. Cool, so the number means nothing.&lt;br&gt;
Ask it to "optimize for this DevOps role" → it adds "Managed production Kubernetes clusters" to a guy who's never opened a terminal. That's not a typo, that's getting someone caught lying in an interview.&lt;br&gt;
So I stopped treating the model like it knows things. I treat its output like a request body from some random client: assume it's garbage until I've checked it.&lt;/p&gt;

&lt;p&gt;Stop asking the model for the score&lt;br&gt;
The score was drifting because I was asking the model to do math, which is the one thing it's bad at. So I just compute the numbers myself first, in boring Python:&lt;/p&gt;

&lt;h1&gt;
  
  
  how many of the top-30 JD keywords actually show up in the resume
&lt;/h1&gt;

&lt;p&gt;keyword_match = 100.0 * sum(1 for k in keywords if _has_word(resume_text, k)) / len(keywords)&lt;/p&gt;

&lt;h1&gt;
  
  
  required skills count double, nice-to-haves count half
&lt;/h1&gt;

&lt;p&gt;denom = len(required) + 0.5 * len(nice)&lt;br&gt;
skills_match = 100.0 * (required_hits + 0.5 * nice_hits) / denom&lt;/p&gt;

&lt;h1&gt;
  
  
  plus cosine(resume_embedding, job_embedding) from text-embedding-3-small
&lt;/h1&gt;

&lt;p&gt;_has_word is just a word-boundary regex that doesn't choke on ci-cd and friends. These numbers are the same every single run.&lt;/p&gt;

&lt;p&gt;Then I give them to the model — but not as the answer. As hints it's allowed to argue with, as long as it says why:&lt;/p&gt;

&lt;p&gt;hint_block = (&lt;br&gt;
    "\nDeterministic hints (you may override but must justify in summary):"&lt;br&gt;
    f"\n- keyword_match={hints.keyword_match}"&lt;br&gt;
    f"\n- skills_match={hints.skills_match}"&lt;br&gt;
    f"\n- embedding_similarity={hints.embedding_similarity}"&lt;br&gt;
)&lt;br&gt;
Now the number is stable, but I still get the stuff a regex can't see — like "you wrote React but the role wants Server Components and your bullets don't back that up." I also hardcode the weighting in the prompt so it can't freelance: score = keyword(0.35) + skills(0.35) + experience(0.20) + format(0.10).&lt;/p&gt;

&lt;p&gt;Make it annoying to lie&lt;br&gt;
The rewriter is the scary one because its whole job is editing text. So the prompt is blunt, and I don't let it write prose — it has to return typed diffs:&lt;/p&gt;

&lt;p&gt;Hard rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Never fabricate experience, dates, companies, or metrics.&lt;/li&gt;
&lt;li&gt;You can rephrase, reorder, tighten, or surface metrics already in the bullets.&lt;/li&gt;
&lt;li&gt;If a required keyword is missing and adding it would be a lie, don't. Say so instead.
changes: [{ section, experience_index?, bullet_index?,
        before: "", after: "", reason }]
Making it echo the exact before for every change means it has to point each edit at something real. Doesn't stop it though. Prompts don't enforce anything — they're just vibes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actual enforcement is on the server&lt;br&gt;
This is the part that matters. After the model answers, I loop its diffs and throw out anything aimed at a role or bullet that doesn't exist:&lt;/p&gt;

&lt;p&gt;for change in raw.changes:&lt;br&gt;
    if change.section == "experience" and change.experience_index is not None:&lt;br&gt;
        if change.experience_index &amp;gt;= len(resume.content.experience):&lt;br&gt;
            log.warning("optimize_drop_invalid_experience_change"); continue&lt;br&gt;
        exp = resume.content.experience[change.experience_index]&lt;br&gt;
        if change.bullet_index is not None and change.bullet_index &amp;gt; len(exp.bullets):&lt;br&gt;
            log.warning("optimize_drop_invalid_bullet_change"); continue&lt;br&gt;
    cleaned_changes.append(change)&lt;br&gt;
Model tries to edit a 4th job on a 3-job resume? Gone, logged, user never sees it. And I never touch the original resume — every optimize writes a new row that points back at the source:&lt;/p&gt;

&lt;p&gt;sb.table("resumes").insert({&lt;br&gt;
    "content": new_content.model_dump(mode="json"),&lt;br&gt;
    "source": "optimized",&lt;br&gt;
    "source_resume_id": source.id,&lt;br&gt;
}).execute()&lt;br&gt;
Worst case is now "here's a draft you can delete," not "the AI wrecked your resume."&lt;/p&gt;

&lt;p&gt;Side effect: it got cheap&lt;br&gt;
Because the inputs are deterministic, caching is trivial — the key is just their hash:&lt;/p&gt;

&lt;p&gt;f"ats:v1:{sha256(resume_id + resume_updated_at + job_description)[:32]}"&lt;br&gt;
Edit the resume, updated_at changes, cache busts, re-score. Otherwise you pay OpenAI once and reopen the result as many times as you want for free.&lt;/p&gt;

&lt;p&gt;What I'd tell past me&lt;br&gt;
Anything you can compute, compute it. Don't make the model guess a number.&lt;br&gt;
Let it override your math, but force it to explain itself.&lt;br&gt;
Structured output isn't a nice-to-have — typed diffs make lying awkward and validation easy.&lt;br&gt;
The prompt is a suggestion. The server is the law.&lt;br&gt;
Never mutate the source. Generate a variant.&lt;br&gt;
Basically: the model is just another untrusted client. Validate its JSON like you'd validate anyone else's.&lt;/p&gt;

&lt;p&gt;— built this in nextstep-today.com &lt;/p&gt;

&lt;h1&gt;
  
  
  ai #resume #llm #nextstep
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>How I Built Two iOS Games in 3 Months as a Solo Developer</title>
      <dc:creator>risola_me</dc:creator>
      <pubDate>Fri, 05 Jun 2026 22:54:59 +0000</pubDate>
      <link>https://dev.to/risola_me_a79eac9d2622b19/how-i-built-two-ios-games-in-3-months-as-a-solo-developer-574f</link>
      <guid>https://dev.to/risola_me_a79eac9d2622b19/how-i-built-two-ios-games-in-3-months-as-a-solo-developer-574f</guid>
      <description>&lt;p&gt;Hi, my name is Ri.&lt;br&gt;
I'm a software developer, but I had never built a game before. No game dev experience, no Unity skills, nothing. Just curiosity and a decision to try.&lt;br&gt;
Three months later I have two games live on the App Store. Here's what I actually did.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why Not Unity?
&lt;/h1&gt;

&lt;p&gt;Everyone says "use Unity for games." I tried. I spent time learning Unity, went through tutorials, understood the basics.&lt;/p&gt;

&lt;p&gt;Then I made a decision — Unity is for my third game. Not the first.&lt;/p&gt;

&lt;p&gt;Why? Because I already knew React Native. And the fastest way to ship is to use what you know. Unity has a steep learning curve and I wanted to actually finish something, not spend 6 months learning a new engine before writing a single line of game logic.&lt;/p&gt;

&lt;p&gt;So I built both games in React Native. Yes, really.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Two Games
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Linko: One Line Path&lt;/strong&gt; — a minimalist puzzle game. You connect all dots on a grid using one continuous line. Sounds simple. Gets brutal fast. 5x5 grids for beginners, up to 9x9 for expert. 5000+ levels total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bricko: Brick Photo Art&lt;/strong&gt; — you build pictures brick by brick, like pixel art. Each picture is a grid of colored bricks you place one by one. Relaxing, satisfying, visual.&lt;/p&gt;

&lt;p&gt;Both are casual. Both are simple in concept. That was intentional.&lt;/p&gt;

&lt;h1&gt;
  
  
  What Was Actually Hard
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Puzzle generation for Linko.&lt;/strong&gt; Generating valid one-line path puzzles that are solvable but not trivial took the most time. I had to make sure every level actually has a solution before it shows up to the player.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance.&lt;/strong&gt; React Native is not a game engine. Rendering hundreds of brick cells smoothly in Bricko required optimization I didn't expect. Flatlist tuning, memoization, reducing re-renders — all of that became real work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Publishing to App Store
&lt;/h1&gt;

&lt;p&gt;This part surprised me with how much time it takes. Screenshots, metadata, age ratings, review guidelines — Apple is detailed. My first submission got rejected for a minor reason. Fixed it, resubmitted, approved.&lt;/p&gt;

&lt;p&gt;The whole process from first submission to live on store took about a week.&lt;/p&gt;

&lt;h1&gt;
  
  
  What I Would Do Differently
&lt;/h1&gt;

&lt;p&gt;Start thinking about marketing on day one, not after launch. I built for 3 months and only started thinking about users after the apps were live. That's backwards. Build in public, share progress, get feedback early.&lt;/p&gt;

&lt;h1&gt;
  
  
  Try Them
&lt;/h1&gt;

&lt;p&gt;If you're curious — both are free to download:&lt;/p&gt;

&lt;p&gt;🧩 Linko: One Line Path&lt;br&gt;
&lt;a href="https://apps.apple.com/us/app/linko-one-line-path/id6774304399" rel="noopener noreferrer"&gt;https://apps.apple.com/us/app/linko-one-line-path/id6774304399&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;🧱 Bricko: Brick Photo Art&lt;br&gt;
&lt;a href="https://apps.apple.com/us/app/bricko-brick-photo-art/id6768135258" rel="noopener noreferrer"&gt;https://apps.apple.com/us/app/bricko-brick-photo-art/id6768135258&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Honest feedback welcome. Good or bad.&lt;/p&gt;

</description>
      <category>gamechallenge</category>
      <category>gamedev</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
