<?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: dbc2201</title>
    <description>The latest articles on DEV Community by dbc2201 (@dbc2201).</description>
    <link>https://dev.to/dbc2201</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F59177%2Ff3893de2-5fa2-484f-bf9c-015d66566c6a.jpeg</url>
      <title>DEV Community: dbc2201</title>
      <link>https://dev.to/dbc2201</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dbc2201"/>
    <language>en</language>
    <item>
      <title>Computers Aren't Calculators — They're Spellbooks</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Wed, 11 Feb 2026 21:54:45 +0000</pubDate>
      <link>https://dev.to/dbc2201/computers-arent-calculators-theyre-spellbooks-oi9</link>
      <guid>https://dev.to/dbc2201/computers-arent-calculators-theyre-spellbooks-oi9</guid>
      <description>&lt;p&gt;We've been thinking about computers wrong. They're not number crunchers or entertainment boxes. They're the most powerful documentation tools ever built — and programmers are the wizards who write spells precise enough to reshape reality.&lt;/p&gt;

&lt;h1&gt;
  
  
  Computers Aren't Calculators — They're Spellbooks
&lt;/h1&gt;

&lt;p&gt;Ask someone what a computer is and you'll hear the same answers: a calculator, an entertainment system, a communication device. And sure, computers do all of those things. But defining a computer by what it &lt;em&gt;does&lt;/em&gt; misses the point of what it &lt;em&gt;is&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Here's a better framing: &lt;strong&gt;a computer is the most powerful documentation tool humans have invented since the book.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That might sound reductive. But sit with it for a moment, and something clicks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything Is a Document
&lt;/h2&gt;

&lt;p&gt;Think about what you actually &lt;em&gt;do&lt;/em&gt; with a computer every day. You write emails. You edit spreadsheets. You update wikis, draft proposals, push code, file tickets. Even the act of "browsing the web" is really just reading documents that other people wrote and published.&lt;/p&gt;

&lt;p&gt;The internet itself is, at its core, a system for sharing documents. HTML was literally invented so physicists could share research papers more easily. Tim Berners-Lee didn't set out to build a platform for cat videos — he wanted better documentation.&lt;/p&gt;

&lt;p&gt;But here's where it gets interesting. Unlike the documents of the past — clay tablets, scrolls, printed books — the documents we create on computers aren't static. They can be &lt;em&gt;executed&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And that changes everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Document That Becomes Real
&lt;/h2&gt;

&lt;p&gt;Consider a Dockerfile.&lt;/p&gt;

&lt;p&gt;If you've never seen one, it reads like a recipe. It says: start with this operating system, install these dependencies, copy these files, run this command. It's plain text. You could print it out and hand it to someone, and they'd understand what it describes.&lt;/p&gt;

&lt;p&gt;But here's the magic — when you give that plain text document to a machine, it doesn't just &lt;em&gt;describe&lt;/em&gt; an environment. It &lt;strong&gt;creates&lt;/strong&gt; one. Hand that same file to a colleague on the other side of the world, and their computer will build the exact same environment as yours. Byte for byte. Dependency for dependency.&lt;/p&gt;

&lt;p&gt;The document &lt;em&gt;became&lt;/em&gt; the thing it described. The map became the territory.&lt;/p&gt;

&lt;p&gt;And this pattern is everywhere in software.&lt;/p&gt;

&lt;p&gt;Source code is a document that happens to be executable. An API specification is a contract between systems, written in a shared language. Infrastructure-as-code is, quite literally, "we wrote down what our servers should look like, and the computers made it real." A &lt;code&gt;.gitignore&lt;/code&gt; file — just a few lines of text — reshapes how an entire project behaves.&lt;/p&gt;

&lt;p&gt;Even a simple configuration file is a document that, once read by the right program, alters the behavior of a machine. We're writing descriptions of reality and handing them to computers that make those descriptions &lt;em&gt;true&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;If that isn't magic, I don't know what is.&lt;/p&gt;

&lt;h2&gt;
  
  
  So What Does That Make Programmers?
&lt;/h2&gt;

&lt;p&gt;If computers are spellbooks, then programmers are the wizards of the modern age. And the metaphor holds up better than you'd expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Languages Are Arcane.&lt;/strong&gt; Wizards study ancient tongues that most people can't read. Programmers study Python, Rust, Haskell, and SQL — languages that look like gibberish to the uninitiated but carry enormous power when wielded correctly. And just like a misplaced syllable in a spell, a missing semicolon can make everything explode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Tools Are Sacred.&lt;/strong&gt; Every wizard has their wand, their staff, their enchanted artifacts. Programmers have their IDEs — and they're &lt;em&gt;fiercely&lt;/em&gt; loyal to them. Vim purists will fight VS Code converts with the same energy as rival magical orders. Customizing your IDE with themes, keybindings, and extensions? That's just enchanting your gear for maximum power.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Elixirs Are Real.&lt;/strong&gt; Every wizard needs potions. Programmers run on caffeine during the day and something stronger after a production incident at 2 AM. The coffee isn't a preference — it's a &lt;em&gt;dependency&lt;/em&gt;. (Pun very much intended.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Career Path Is an Apprenticeship.&lt;/strong&gt; Junior developers are apprentices, copying spells from Stack Overflow without fully understanding the incantations. Mid-level developers are journeyman mages who can adapt known spells to new situations. Senior developers are the grey-bearded wizards who've seen things break in ways that shouldn't be physically possible — and somehow fixed them with three lines of code and a prayer.&lt;/p&gt;

&lt;p&gt;And the software architect? That's the wizard in the tower, drawing diagrams on enchanted whiteboards that nobody else can fully interpret but somehow keep the entire kingdom running.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deeper Truth
&lt;/h2&gt;

&lt;p&gt;Here's what makes this more than just a fun metaphor.&lt;/p&gt;

&lt;p&gt;When we write software, we're not just "telling computers what to do." We're authoring documents so precise that machines can interpret them and reshape the digital world accordingly. And increasingly, that digital world &lt;em&gt;is&lt;/em&gt; the world — it's how we bank, communicate, navigate, learn, and work.&lt;/p&gt;

&lt;p&gt;Programming is, at its heart, an act of documentation. It's the practice of writing things down so carefully, so unambiguously, that reality bends to match what you wrote.&lt;/p&gt;

&lt;p&gt;Books changed the world by letting us preserve and share knowledge across time and space. Computers took that idea and added a twist: &lt;strong&gt;the knowledge executes itself.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We went from "write it down so others can learn" to "write it down so the universe rearranges."&lt;/p&gt;

&lt;h2&gt;
  
  
  So the Next Time Someone Asks What You Do...
&lt;/h2&gt;

&lt;p&gt;Don't say you "work with computers." Don't say you "write code."&lt;/p&gt;

&lt;p&gt;Say you write spells in arcane languages, using enchanted tools, fueled by ancient elixirs — and when the incantation is correct, reality changes.&lt;/p&gt;

&lt;p&gt;It's technically accurate. And honestly? It sounds way cooler at parties.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your favorite "spell" you've ever written — the piece of code that felt like actual magic when it worked? Drop it in the comments. Bonus points if it broke something spectacular first.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Developer You Could Become: Reclaiming India's Place in Open Source</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Sat, 17 Jan 2026 12:13:13 +0000</pubDate>
      <link>https://dev.to/dbc2201/the-developer-you-could-become-reclaiming-indias-place-in-open-source-4jf5</link>
      <guid>https://dev.to/dbc2201/the-developer-you-could-become-reclaiming-indias-place-in-open-source-4jf5</guid>
      <description>&lt;p&gt;&lt;em&gt;A roadmap for building genuine contributors—from someone who's walked the same path&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Before We Begin: I Need to Tell You Something
&lt;/h2&gt;

&lt;p&gt;In 2018, I was exactly where you might be right now.&lt;/p&gt;

&lt;p&gt;I was chasing credentials. Hunting for that one thing that would make my resume stand out. And then I cracked the Google India Challenge Scholarship—selected through Udacity, backed by Google Inc.&lt;/p&gt;

&lt;p&gt;That moment changed my trajectory. But here's what I didn't understand back then: &lt;strong&gt;the scholarship didn't make me a better developer. The learning that came after did.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm telling you this because I need you to trust me when I say: I understand the pressure you're under. The placement anxiety. The family expectations. The feeling that everyone around you has figured out some secret you haven't.&lt;/p&gt;

&lt;p&gt;I've been there. I've felt that desperate need to have &lt;em&gt;something&lt;/em&gt; on my resume that proves I belong.&lt;/p&gt;

&lt;p&gt;But I've also seen what happens when that desperation leads people down the wrong path. And I've spent years training thousands of engineers, watching some flourish and others struggle—not because of talent, but because of the choices they made early on.&lt;/p&gt;

&lt;p&gt;So let me share what I wish someone had told me.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Developer They Remember
&lt;/h2&gt;

&lt;p&gt;Somewhere in Bangalore, a maintainer of a popular web framework is scrolling through pull requests on a Saturday morning. Coffee in hand, bleary-eyed, volunteering their weekend because they love the project they've built over seven years.&lt;/p&gt;

&lt;p&gt;They pause at a PR titled "Update README."&lt;/p&gt;

&lt;p&gt;It adds one line: their name. Just... their name. Added to the heading of the README file.&lt;/p&gt;

&lt;p&gt;They sigh. Close. Mark as spam. Lock thread. Report user.&lt;/p&gt;

&lt;p&gt;This ritual will repeat eleven more times before lunch. Each notification pings 485 watchers. Each one costs five minutes of attention. Each one makes them wonder why they bother.&lt;/p&gt;




&lt;p&gt;Now imagine a different PR. Same maintainer, same Saturday morning. But this one fixes a bug the contributor actually encountered. The commit message explains the problem clearly. The code is tested. The contributor has been in the community for three months, helping newcomers in Discord, answering questions they once asked themselves.&lt;/p&gt;

&lt;p&gt;The maintainer smiles. Merges. Remembers why open source still matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which contributor do you want to be?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Uncomfortable Truth: What's Actually Happening Right Now
&lt;/h2&gt;

&lt;p&gt;I need to show you something that's been happening—and is &lt;em&gt;still&lt;/em&gt; happening as you read this.&lt;/p&gt;

&lt;h3&gt;
  
  
  The ExpressJS Incident (It's Not Ancient History)
&lt;/h3&gt;

&lt;p&gt;ExpressJS is one of the most popular Node.js frameworks in the world. Millions of applications run on it. And right now, if you go to their GitHub repository and look at closed pull requests, you'll find &lt;strong&gt;pages upon pages&lt;/strong&gt; of PRs titled "Update README" or "Update README.md."&lt;/p&gt;

&lt;p&gt;What are these PRs doing?&lt;/p&gt;

&lt;p&gt;Adding names. Just names. "Swapnil." "Adarsh." "Speedy Navigation." Contributors simply adding their own names to the H1 heading of the README file.&lt;/p&gt;

&lt;p&gt;As one developer who investigated this &lt;a href="https://www.youtube.com/watch?v=ukRizbUhfeM" rel="noopener noreferrer"&gt;pointed out&lt;/a&gt;, searching "Update README" on that repository shows &lt;strong&gt;20 pages of mostly garbage PRs&lt;/strong&gt; mixed with a few legitimate ones. Another analysis found that approximately &lt;strong&gt;22.8% of all open PRs&lt;/strong&gt; on ExpressJS are these spammy README updates.&lt;/p&gt;

&lt;p&gt;This isn't a bot attack. These are real people—our fellow Indian developers—following tutorials without thinking.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Source: A Tutorial Watched by Millions
&lt;/h3&gt;

&lt;p&gt;Here's what happened, and I'm sharing this not to shame anyone, but because you need to understand the mechanics of how this works.&lt;/p&gt;

&lt;p&gt;A popular Indian educational YouTube channel with over 7 million subscribers published a Git and GitHub tutorial. It's been watched over 5.6 million times. And 99% of the video is genuinely helpful.&lt;/p&gt;

&lt;p&gt;But there's a 3-minute segment near the end where the instructor demonstrates how to fork a repository. She uses the &lt;strong&gt;official ExpressJS repository&lt;/strong&gt; for the demonstration. She shows how to edit the README (adding a name), and how to create a pull request.&lt;/p&gt;

&lt;p&gt;Now, here's the thing: &lt;strong&gt;she explicitly warns viewers not to actually submit this PR.&lt;/strong&gt; She says, verbatim: &lt;em&gt;"This is a useless commit... do not actually create this pull request."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;But here's what happens with a video that has 5.6 million views:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audience retention graphs show viewers often stop listening carefully by the end of tutorials&lt;/li&gt;
&lt;li&gt;Many viewers watch with the sound low, just following the visual steps&lt;/li&gt;
&lt;li&gt;Even if only &lt;strong&gt;1%&lt;/strong&gt; ignore the verbal warning and follow the visual steps...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's &lt;strong&gt;56,000 potential spam PRs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As &lt;a href="https://www.youtube.com/watch?v=7Thqw58L8gw" rel="noopener noreferrer"&gt;one Indian developer&lt;/a&gt; doing the math put it: with 1.3 million views at the time he checked, even 1% means 13,000 spam PRs for maintainers to deal with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Keeps Happening (Year After Year)
&lt;/h3&gt;

&lt;p&gt;What makes this particularly painful is that &lt;strong&gt;this issue was identified over two years ago.&lt;/strong&gt; Multiple prominent developers made videos about it. Comments on the original tutorial warn viewers not to create test PRs on official repos.&lt;/p&gt;

&lt;p&gt;YouTube Studio allows creators to &lt;strong&gt;trim segments&lt;/strong&gt; out of existing videos without re-uploading. The problematic 3-minute section could have been removed with a few clicks.&lt;/p&gt;

&lt;p&gt;It wasn't.&lt;/p&gt;

&lt;p&gt;And so every year—especially during Hacktoberfest season, or when new college terms begin—the spam comes in waves. &lt;a href="https://www.youtube.com/watch?v=YFkeOBqfQBw" rel="noopener noreferrer"&gt;ThePrimeagen&lt;/a&gt;, a popular developer and content creator, documented that &lt;strong&gt;two years later, it's still happening.&lt;/strong&gt; The spam has spread beyond ExpressJS to other repositories, including Node.js itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Real Consequences (They're Worse Than You Think)
&lt;/h3&gt;

&lt;p&gt;Here's what many students don't realize:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. You can get your entire GitHub account banned.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GitHub's Terms of Service explicitly prohibit "opening empty or meaningless issues or pull requests." As &lt;a href="https://www.youtube.com/watch?v=jDbRekCtVRw" rel="noopener noreferrer"&gt;Hitesh Choudhary (Chai aur Code)&lt;/a&gt; explained in a video about this issue, getting flagged as a spammer doesn't just lock you out temporarily—it can &lt;strong&gt;destroy your reputation as a developer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One user's account got suspended, locking them out of all their repositories—including sensitive ones—with no way to back them up. They eventually got it unlocked after contacting support, but the warning is clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. CI/CD costs real money.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you open a PR, it often triggers automated tests (Continuous Integration) on the maintainer's servers. Thousands of garbage PRs mean &lt;strong&gt;wasted computational power and money&lt;/strong&gt; on changes that add zero value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It reinforces the worst stereotypes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because of India's large population, even a small percentage of bad actors creates a massive volume of spam. This reinforces negative stereotypes about Indian developers in the global community—stereotypes that affect &lt;strong&gt;all of us&lt;/strong&gt;, including those doing excellent work.&lt;/p&gt;




&lt;h2&gt;
  
  
  It's Not You—It's a System That Shaped You
&lt;/h2&gt;

&lt;p&gt;Now, before you think I'm here to lecture you, let me say something important:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you've ever felt the urge to just "get something on GitHub," you're not broken.&lt;/strong&gt; You're responding rationally to a broken system.&lt;/p&gt;

&lt;p&gt;Consider the math. Major coding programs offer stipends of ₹1.2-2.5 lakh over a few months. The average engineering internship pays around ₹17,778/month. Even elite internships cap around ₹50,000-80,000/month.&lt;/p&gt;

&lt;p&gt;Colleges celebrate program selections as institutional achievements. Your seniors tell you that a "green contribution graph" matters for placements. YouTube thumbnails scream about earning lakhs from stipends.&lt;/p&gt;

&lt;p&gt;The economic logic is overwhelming. The social pressure is real.&lt;/p&gt;

&lt;p&gt;And here's the deeper problem: &lt;a href="https://www.dqindia.com/scary-95-engineers-in-india-are-not-employable/" rel="noopener noreferrer"&gt;multiple studies&lt;/a&gt; have documented that our engineering education system "still leans heavily on classroom lectures, textbooks, and rote learning." Open source isn't in most curricula. Git workflows, community etiquette, how to read unfamiliar codebases—these skills are left for you to figure out on your own.&lt;/p&gt;

&lt;p&gt;So when a YouTube video shows a shortcut, is it any surprise thousands take it?&lt;/p&gt;

&lt;p&gt;But here's what I want you to understand: &lt;strong&gt;the shortcuts don't lead where you think they lead.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Chasing green contribution squares or free t-shirts via spam PRs has—and I cannot stress this enough—&lt;strong&gt;0% impact on your actual career growth.&lt;/strong&gt; The hiring managers I talk to don't count contribution squares. They look at the actual code. They check if your PRs were merged or closed as spam. They notice if your "contributions" are just README edits.&lt;/p&gt;

&lt;p&gt;The irony is brutal: the very thing you're doing to improve your resume could be what destroys it.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Moment of Encouragement (Yes, Really)
&lt;/h2&gt;

&lt;p&gt;Here's something that might actually make you feel better.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=7Thqw58L8gw" rel="noopener noreferrer"&gt;One developer&lt;/a&gt; who analyzed this situation offered a surprisingly motivating perspective to skilled developers worried about market saturation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Your competition consists of these people who blindly copy-paste without thinking. If you have genuine skills and critical thinking, you have nothing to worry about—because the vast majority of the crowd lacks these basics."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read that again.&lt;/p&gt;

&lt;p&gt;The bar is not as high as you think. You don't need to be a genius. You don't need to attend an IIT. You don't need to have been coding since age 10.&lt;/p&gt;

&lt;p&gt;You just need to be someone who &lt;strong&gt;thinks before they act.&lt;/strong&gt; Someone who reads the instructions. Someone who asks, "Does this actually add value?"&lt;/p&gt;

&lt;p&gt;That alone puts you ahead of a terrifying percentage of your peers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Actually Works: Lessons From Those Who Got It Right
&lt;/h2&gt;

&lt;p&gt;Let me tell you about programs that successfully build real contributors.&lt;/p&gt;

&lt;h3&gt;
  
  
  Outreachy's Three-Phase Model
&lt;/h3&gt;

&lt;p&gt;Outreachy places contributors into open source projects. Here's how selection works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1:&lt;/strong&gt; You write essays explaining your genuine interest. They explicitly warn against AI-generated content. They're looking for authentic motivation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2:&lt;/strong&gt; You spend four weeks &lt;strong&gt;actually contributing&lt;/strong&gt;. Not writing proposals about what you might do someday—doing the work, now, in public. This is the brutal but beautiful filter: it selects for people who can work independently, communicate publicly, and prioritize quality over quantity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3:&lt;/strong&gt; Mentors rank applicants 1-5 based on demonstrated work. "5 = Amazing applicant, could become maintainer, extensive high-quality contributions."&lt;/p&gt;

&lt;p&gt;The key insight: &lt;strong&gt;selection happens after demonstrated contribution, not before.&lt;/strong&gt; You can't game a system that requires you to actually do the thing.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Apache Way
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.apache.org/theapacheway/" rel="noopener noreferrer"&gt;Apache Software Foundation&lt;/a&gt; uses a similar philosophy. Contributors progress through levels: User → Contributor → Committer → Project Management Committee Member → Foundation Member.&lt;/p&gt;

&lt;p&gt;Their core principle is "Community Over Code"—they'd rather have a healthy community with mediocre code than brilliant code maintained by toxic people. Because over time, healthy communities produce better code anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Post-Crisis Hacktoberfest
&lt;/h3&gt;

&lt;p&gt;After the 2020 spam crisis (which I'm sure you've heard about—it was covered by &lt;a href="https://www.theregister.com/2020/10/01/digitalocean_hacktoberfest_pull_request_spam/" rel="noopener noreferrer"&gt;The Register&lt;/a&gt;, &lt;a href="https://www.infoq.com/news/2020/10/hacked-off-hacktoberfest/" rel="noopener noreferrer"&gt;InfoQ&lt;/a&gt;, and sparked the #shitoberfest hashtag), the annual open-source celebration made structural changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintainers must &lt;strong&gt;opt-in&lt;/strong&gt; by adding a specific tag&lt;/li&gt;
&lt;li&gt;Contributions only count when maintainers explicitly label them "accepted"&lt;/li&gt;
&lt;li&gt;Users with multiple spam PRs are &lt;strong&gt;permanently banned&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;An advisory council provides ongoing feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By 2022, the same program saw 146,891 participants from 194 countries producing 335,000 contributions—with measurably higher quality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson: give gatekeepers control, require demonstrated work before recognition, and make gaming harder than genuine contribution.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding What You're Walking Into
&lt;/h2&gt;

&lt;p&gt;Before you submit your first pull request, I need you to understand something that will change how you approach every interaction.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href="https://tidelift.com/learn/open-source-maintainers" rel="noopener noreferrer"&gt;2024 Tidelift Maintainer Report&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;60% of maintainers are not paid&lt;/strong&gt; for their work. At all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;60% have quit or considered quitting&lt;/strong&gt; maintaining their projects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;44% cite burnout&lt;/strong&gt; as the reason.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are volunteers. They have day jobs, families, health problems, grief. They maintain code that powers companies worth billions—for free, on weekends, often alone.&lt;/p&gt;

&lt;p&gt;As one developer &lt;a href="https://www.youtube.com/watch?v=ukRizbUhfeM" rel="noopener noreferrer"&gt;put it bluntly&lt;/a&gt;: "Open Source is not a playground for absolute beginners to test buttons."&lt;/p&gt;

&lt;p&gt;That sounds harsh. But think about it this way: would you walk into a stranger's workshop and start randomly pressing buttons on their equipment? Would you "test" hacking on Google's servers just to learn how penetration testing works?&lt;/p&gt;

&lt;p&gt;The same principle applies here.&lt;/p&gt;

&lt;p&gt;When you ping a maintainer asking to be assigned an issue you haven't researched... When you submit a PR that doesn't follow the contribution guidelines you didn't read... When you ask questions the documentation already answers...&lt;/p&gt;

&lt;p&gt;You're not just wasting time. You're contributing to the burnout that's killing the ecosystem that benefits all of us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But flip it around:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you show up having done your homework... When your PR is small, focused, well-tested, and solves a real problem... When you help newcomers instead of just asking for help...&lt;/p&gt;

&lt;p&gt;You become the kind of person maintainers want to mentor. The kind who might, someday, become a maintainer yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Personal Roadmap: Four Phases to Becoming a Real Contributor
&lt;/h2&gt;

&lt;p&gt;Let me give you the framework I wish I'd had when I started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Foundation (Before You Touch Any Codebase)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Technical skills to build:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git proficiency: branching, rebasing, resolving conflicts, writing meaningful commit messages&lt;/li&gt;
&lt;li&gt;Ability to set up development environments from documentation alone&lt;/li&gt;
&lt;li&gt;Comfort reading code you didn't write&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mindset shifts to make:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Read the manual before asking" becomes your default&lt;/li&gt;
&lt;li&gt;You accept that maintainers don't owe you anything&lt;/li&gt;
&lt;li&gt;You commit to contributing only to projects you actually use or care about&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practice first:&lt;/strong&gt; Create your own repositories. Test features there. Break things in your own sandbox—not in someone else's production codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkpoint:&lt;/strong&gt; You can clone a project, set it up locally, run the tests, and navigate the codebase without asking anyone for help.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 2: Community Orientation (Observe Before You Participate)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Activities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Join the project's communication channels (Discord, Slack, mailing lists)&lt;/li&gt;
&lt;li&gt;Read the last 50 issues and 20 merged PRs&lt;/li&gt;
&lt;li&gt;Identify who the maintainers are and how they communicate&lt;/li&gt;
&lt;li&gt;Understand the project's rhythms: when are people active? How long do reviews take?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The silent observation period matters.&lt;/strong&gt; You're not just learning the codebase—you're learning the culture. Every community has unwritten rules. The only way to learn them is to watch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkpoint:&lt;/strong&gt; You can explain the project's governance structure, name the active maintainers, and describe what a good contribution looks like for this specific project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Quality Contribution (Your First Real Work)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Good first contributions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fix a bug you actually encountered while using the project&lt;/li&gt;
&lt;li&gt;Improve documentation that genuinely confused you&lt;/li&gt;
&lt;li&gt;Write tests for untested code paths&lt;/li&gt;
&lt;li&gt;Respond to issues with helpful debugging context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quality standards:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PRs are small (50-200 lines ideally)&lt;/li&gt;
&lt;li&gt;Commit messages follow the project's conventions (check their CONTRIBUTING.md)&lt;/li&gt;
&lt;li&gt;You respond to review feedback completely and promptly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The key test:&lt;/strong&gt; You don't need the certificate to feel good about the work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As &lt;a href="https://www.youtube.com/watch?v=YFkeOBqfQBw" rel="noopener noreferrer"&gt;ThePrimeagen advised&lt;/a&gt;: Join the Discord of a project you like. Ask for advice on which issue to tackle. Spend hours debugging, adding print statements, understanding the code. Submit a PR with actual value.&lt;/p&gt;

&lt;p&gt;"Real joy in programming comes from solving actual problems, not from adding your name to a README."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Checkpoint:&lt;/strong&gt; You have at least one contribution merged with positive feedback from a maintainer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 4: Sustained Participation (Where Real Growth Happens)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Activities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Move beyond "good first issues" to meatier challenges&lt;/li&gt;
&lt;li&gt;Help review other people's PRs (even without commit access)&lt;/li&gt;
&lt;li&gt;Answer questions from newer contributors&lt;/li&gt;
&lt;li&gt;Take on documentation ownership or triage duties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6-month metrics that matter:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are you still contributing to the same project?&lt;/li&gt;
&lt;li&gt;Do maintainers recognize your name?&lt;/li&gt;
&lt;li&gt;Have you helped anyone else get started?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Checkpoint:&lt;/strong&gt; Someone refers to you as a regular contributor, not a drive-by participant.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start Today: Your First Three Actions
&lt;/h2&gt;

&lt;p&gt;Don't wait until you "know enough." Here's what you can do this week:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Pick ONE tool you use daily that's open source
&lt;/h3&gt;

&lt;p&gt;Maybe it's VS Code, a testing library, or a linter. Don't pick something famous just because it's famous. Pick something you actually use—because you'll understand the problems it has.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Find and join their community
&lt;/h3&gt;

&lt;p&gt;Look for a Discord, Slack, or discussion forum. Introduce yourself briefly. Then lurk. Read. Learn how people talk to each other there.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Set up the project locally
&lt;/h3&gt;

&lt;p&gt;Clone it. Read the README. Follow the setup instructions. Run the tests. If something in the docs confused you, &lt;strong&gt;write it down&lt;/strong&gt;—that confusion is your first potential contribution.&lt;/p&gt;

&lt;p&gt;That's it. No PRs yet. No issues opened. Just presence, observation, and preparation.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Word About Your "Competition"
&lt;/h2&gt;

&lt;p&gt;I want to leave you with something that might fundamentally change how you see your situation.&lt;/p&gt;

&lt;p&gt;You're worried about saturation. About competition. About whether there's room for you in tech.&lt;/p&gt;

&lt;p&gt;But think about who your "competition" actually is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;People who submit PRs adding their name to README files&lt;/li&gt;
&lt;li&gt;People who follow tutorials one-to-one without thinking&lt;/li&gt;
&lt;li&gt;People who can't understand the purpose of a Pull Request&lt;/li&gt;
&lt;li&gt;People who chase green squares instead of actual skills&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As one developer &lt;a href="https://www.youtube.com/watch?v=7Thqw58L8gw" rel="noopener noreferrer"&gt;observed&lt;/a&gt;: if you have genuine skills and critical thinking, you have nothing to worry about—because the vast majority of the "crowd" lacks these basics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The bar is not adding your name to ExpressJS. The bar is solving real problems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And that bar? It's completely achievable. Not because you need to be exceptional—but because so many people aren't even trying to clear it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources for the Journey
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Learning Git properly:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Oh My Git! (interactive game): &lt;a href="https://ohmygit.org/" rel="noopener noreferrer"&gt;https://ohmygit.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Git Katas (exercises): &lt;a href="https://github.com/eficode-academy/git-katas" rel="noopener noreferrer"&gt;https://github.com/eficode-academy/git-katas&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Understanding open source culture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Apache Way: &lt;a href="https://www.apache.org/theapacheway/" rel="noopener noreferrer"&gt;https://www.apache.org/theapacheway/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;First Contributions: &lt;a href="https://firstcontributions.github.io/" rel="noopener noreferrer"&gt;https://firstcontributions.github.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Finding projects that welcome newcomers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good First Issues: &lt;a href="https://goodfirstissue.dev/" rel="noopener noreferrer"&gt;https://goodfirstissue.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;CodeTriage: &lt;a href="https://www.codetriage.com/" rel="noopener noreferrer"&gt;https://www.codetriage.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Indian open source communities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;FOSS United: &lt;a href="https://fossunited.org/" rel="noopener noreferrer"&gt;https://fossunited.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;amFOSS (Amrita): &lt;a href="https://amfoss.in/" rel="noopener noreferrer"&gt;https://amfoss.in/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A Final Word
&lt;/h2&gt;

&lt;p&gt;I started this by telling you about my Google India Challenge Scholarship in 2018. Let me tell you what I didn't say:&lt;/p&gt;

&lt;p&gt;That credential opened doors. But what kept those doors open was the work I did after—the skills I built, the communities I joined, the problems I solved. The credential was a starting point, not a destination.&lt;/p&gt;

&lt;p&gt;If I had gotten that scholarship by gaming the system, by cheating, by submitting spam... it would have been worthless. Because the moment I was asked to actually do something, I would have been exposed.&lt;/p&gt;

&lt;p&gt;The path I've described in this post is harder. It takes longer. It requires you to care about more than credentials.&lt;/p&gt;

&lt;p&gt;But at the end of it, you'll be someone who can actually build things. Someone maintainers trust. Someone who gets opportunities not because of resume keywords, but because people have seen your work.&lt;/p&gt;

&lt;p&gt;Someone who, when they say "I'm an open source contributor," actually is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The developer you could become is waiting. Start today.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If this resonated with you, share it with a friend who needs to hear it. And if you're already doing the hard work of genuine contribution—thank you. The community needs more people like you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;— Divyansh Bhardwaj&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Corporate &amp;amp; Technical Trainer | Google India Challenge Scholarship Recipient 2018&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Teaching engineers to think, not just code&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>motivation</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The Java Developer's Roadmap for 2026: From First Program to Production-Ready Professional</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Fri, 09 Jan 2026 11:23:22 +0000</pubDate>
      <link>https://dev.to/dbc2201/the-java-developers-roadmap-for-2026-from-first-program-to-production-ready-professional-41ng</link>
      <guid>https://dev.to/dbc2201/the-java-developers-roadmap-for-2026-from-first-program-to-production-ready-professional-41ng</guid>
      <description>&lt;p&gt;&lt;em&gt;A comprehensive, modern guide to becoming a confident Java developer — built from 10+ years of training thousands of students across India and beyond.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR: The 7 Phases at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Focus&lt;/th&gt;
&lt;th&gt;Key Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tools &amp;amp; Environment&lt;/td&gt;
&lt;td&gt;Java 25, IntelliJ, Git ready&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Computational Thinking&lt;/td&gt;
&lt;td&gt;Logic before language&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Methods &amp;amp; Functions&lt;/td&gt;
&lt;td&gt;Decompose problems&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3A&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Core OOP&lt;/td&gt;
&lt;td&gt;Classes, inheritance, interfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Advanced OOP&lt;/td&gt;
&lt;td&gt;Exceptions, threads, generics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Modern Java&lt;/td&gt;
&lt;td&gt;Collections, streams, records, virtual threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data &amp;amp; Persistence&lt;/td&gt;
&lt;td&gt;SQL, JDBC, JPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot 4&lt;/td&gt;
&lt;td&gt;REST APIs, AI integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Professional Practices&lt;/td&gt;
&lt;td&gt;Testing, Docker, CI/CD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Time investment:&lt;/strong&gt; 6-12 months of consistent practice. &lt;strong&gt;No shortcuts.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  This Post is For You If...
&lt;/h2&gt;

&lt;p&gt;You want to learn Java — properly. Not the "watch a 4-hour crash course and call yourself a developer" kind of learning. The real kind. The kind where you actually understand what you're writing, why you're writing it, and how to build software that solves real problems.&lt;/p&gt;

&lt;p&gt;It doesn't matter who you are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;first-year engineering student&lt;/strong&gt; staring at your first programming course&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;final-year student&lt;/strong&gt; panicking about placements&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;working professional&lt;/strong&gt; from a non-tech background looking to switch careers&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;self-taught learner&lt;/strong&gt; who's been "learning Java" for months but still feels lost&lt;/li&gt;
&lt;li&gt;Someone who &lt;strong&gt;already "knows" Java&lt;/strong&gt; from college but can't build anything real with it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Java doesn't care about your background. Neither does this roadmap.&lt;/p&gt;

&lt;p&gt;What matters is your willingness to put in the work. &lt;strong&gt;There are no shortcuts.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Project: JanSahayak (जनसहायक — Citizen's Assistant)
&lt;/h2&gt;

&lt;p&gt;Before we dive into the phases, let me tell you what you'll build. Because learning without purpose is just academic exercise.&lt;/p&gt;

&lt;p&gt;I refuse to make you build a todo app. It's the most overused, uninspiring project in programming education. Nobody wants to look at it. Nobody is impressed by it.&lt;/p&gt;

&lt;p&gt;Instead, you'll build &lt;strong&gt;JanSahayak&lt;/strong&gt; — an AI-powered platform that helps citizens navigate government schemes, understand required documents, and access local services. This project evolves through all phases of the roadmap:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Phase 1&lt;/td&gt;
&lt;td&gt;CLI tool: "What documents do I need for X?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 2&lt;/td&gt;
&lt;td&gt;Refactored with methods, file I/O for data storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 3&lt;/td&gt;
&lt;td&gt;OOP design: Citizen, Document, Scheme, Service classes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 4&lt;/td&gt;
&lt;td&gt;Modern Java: Records for DTOs, Streams for processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 5&lt;/td&gt;
&lt;td&gt;Database integration with PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 6&lt;/td&gt;
&lt;td&gt;REST API with Spring Boot 4, AI-powered with Spring AI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Phase 7&lt;/td&gt;
&lt;td&gt;Dockerized, tested, deployed with CI/CD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Choose Your Own Adventure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't like JanSahayak? Choose a different domain with the same architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;KisanMitra (किसानमित्र)&lt;/strong&gt; — Agricultural advisory platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SwasthyaSathi (स्वास्थ्यसाथी)&lt;/strong&gt; — Health information assistant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VidyaSahay (विद्यासहाय)&lt;/strong&gt; — Educational resource navigator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RojgarDoot (रोज़गारदूत)&lt;/strong&gt; — Job/skill matching platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or pick a problem from the &lt;strong&gt;&lt;a href="https://sih.gov.in/" rel="noopener noreferrer"&gt;Smart India Hackathon&lt;/a&gt;&lt;/strong&gt; — real problems that real organizations need solved. Check out the &lt;a href="https://www.sih.gov.in/sih2025PS" rel="noopener noreferrer"&gt;problem statements&lt;/a&gt; for inspiration.&lt;/p&gt;

&lt;p&gt;The architecture blueprint remains the same. The domain is your choice.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Question I've Been Asked 10,000 Times
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"Sir, how do I get started with Java?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've been teaching and training developers since 2014 (officially since 2018), and this question has never stopped coming. Not once. Every batch, every bootcamp, every institution — the same question, the same anxiety in the eyes.&lt;/p&gt;

&lt;p&gt;And here's the thing: it's a &lt;em&gt;good&lt;/em&gt; question. Perhaps the most important question a beginner can ask. Because how you start determines everything that follows.&lt;/p&gt;

&lt;p&gt;Back in 2021, I wrote a roadmap that went viral in the Java community. Every major Java Twitter account shared it. Students from across the world reached out saying it helped them find direction.&lt;/p&gt;

&lt;p&gt;But 2021 was a different world. The job market was different. The technology was different. &lt;strong&gt;Java itself is different now.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So here we are — a refreshed, battle-tested roadmap for 2026 and beyond.&lt;/p&gt;




&lt;h2&gt;
  
  
  2021 vs 2026: How the Industry Has Changed
&lt;/h2&gt;

&lt;p&gt;Five years might not seem like much, but in technology, it's an eternity. Here's what's changed since my original roadmap:&lt;/p&gt;

&lt;h3&gt;
  
  
  What a Java Developer Needed in 2021
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;2021 Requirements&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Java Version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Java 11 or 16, with Java 8 still common&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Approach&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Start with &lt;code&gt;public static void main&lt;/code&gt;, explain ceremony later&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spring Version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot 2.x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Not expected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Traditional threads, ExecutorService&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nice to have, often learned "later"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DevOps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optional for beginners&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Problem Solving&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Focus on competitive programming sites&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What a Java Developer Needs in 2026
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;2026 Requirements&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Java Version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://dev.java/learn/" rel="noopener noreferrer"&gt;Java 25 LTS&lt;/a&gt; (released September 16, 2025) with modern features as defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning Approach&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Computation-first with &lt;a href="https://dev.java/learn/simple-main-method/" rel="noopener noreferrer"&gt;simplified syntax&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spring Version&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://spring.io/blog/2025/11/20/spring-boot-4-0-0-available-now" rel="noopener noreferrer"&gt;Spring Boot 4&lt;/a&gt; (released November 20, 2025) with &lt;a href="https://spring.io/projects/spring-framework" rel="noopener noreferrer"&gt;Spring Framework 7&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI Integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://spring.io/projects/spring-ai" rel="noopener noreferrer"&gt;Spring AI&lt;/a&gt; is table stakes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://dev.java/learn/virtual-threads/" rel="noopener noreferrer"&gt;Virtual Threads&lt;/a&gt; (stable), &lt;a href="https://openjdk.org/jeps/505" rel="noopener noreferrer"&gt;Structured Concurrency&lt;/a&gt; (preview)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Testing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Non-negotiable from day one; &lt;a href="https://junit.org/junit5/" rel="noopener noreferrer"&gt;JUnit 6&lt;/a&gt; is the new default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DevOps&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Docker, CI/CD expected for entry-level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Problem Solving&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Real-world projects with hackathon-style challenges&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Skill Gap Reality
&lt;/h3&gt;

&lt;p&gt;In 2021, knowing Core Java well could get you interviews. In 2026, that's just the starting line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New mandatory skills:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI Integration&lt;/strong&gt;: Companies expect you to use AI tools effectively AND build AI-powered features&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modern Java Features&lt;/strong&gt;: Records, pattern matching, sealed classes aren't "advanced" — they're baseline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual Threads&lt;/strong&gt;: The old thread-pool-for-everything approach is outdated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Design&lt;/strong&gt;: REST isn't enough; you need API versioning, GraphQL awareness&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt;: OpenTelemetry, metrics, and tracing from day one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Skills that evolved:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spring&lt;/strong&gt;: From Boot 2.x to Boot 4.0 — completely different paradigm with &lt;a href="https://spring.io/blog/2025/10/28/modularizing-spring-boot" rel="noopener noreferrer"&gt;modular codebase&lt;/a&gt;, &lt;a href="https://spring.io/blog/2025/11/12/null-safe-applications-with-spring-boot-4" rel="noopener noreferrer"&gt;null safety&lt;/a&gt;, and &lt;a href="https://spring.io/blog/2025/09/23/http-service-client-enhancements" rel="noopener noreferrer"&gt;HTTP Service Clients&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt;: From "write tests when you have time" to TDD being expected; JUnit 6 is now the default in Spring Boot 4&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version Control&lt;/strong&gt;: From basic Git to full PR workflows with conventional commits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The uncomfortable truth:&lt;/strong&gt; The 2021 roadmap's four stages (Syntax → OOP → Collections → Problem Solving) produced developers who could pass coding tests. The 2026 reality demands developers who can build, deploy, and maintain production software.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Continuous Learning is Non-Negotiable
&lt;/h3&gt;

&lt;p&gt;Look at this progression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2021: Java 16, Spring Boot 2.4, traditional threads
2022: Java 17 LTS, Spring Boot 2.7, virtual threads preview
2023: Java 21 LTS, Spring Boot 3.0, pattern matching stable
2024: Java 23, Spring Boot 3.3, structured concurrency preview
2025: Java 25 LTS, Spring Boot 4.0, AI integration standard
2026: You are here. What's next?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developers who thrived through these changes weren't necessarily the smartest — they were the ones who &lt;strong&gt;never stopped learning&lt;/strong&gt;. They stayed curious. They didn't just learn Java once; they evolved with it.&lt;/p&gt;

&lt;p&gt;This industry will leave you behind if you stand still. That's not meant to scare you — it's meant to prepare you. The good news? Java has been evolving beautifully, and each evolution has made developer lives &lt;em&gt;easier&lt;/em&gt;, not harder.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before We Begin: Some Hard Truths
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Truth #1: Learning a Programming Language is Like Learning a Spoken Language
&lt;/h3&gt;

&lt;p&gt;Your brain needs to rewire itself. You need to think differently. You'll be frustrated, confused, and occasionally wonder if you're smart enough for this.&lt;/p&gt;

&lt;p&gt;You are. Everyone goes through this. The difference between those who succeed and those who give up isn't intelligence — it's persistence. The willingness to sit with confusion until clarity arrives. The discipline to practice even when it's boring.&lt;/p&gt;

&lt;h3&gt;
  
  
  Truth #2: There is No "Advanced" Java
&lt;/h3&gt;

&lt;p&gt;Let me say something that might surprise you: the distinction between "Core Java" and "Advanced Java" is largely a market gimmick.&lt;/p&gt;

&lt;p&gt;Yes, it made sense in the 1990s when object-oriented programming was a paradigm shift. But today? Every modern language has adopted these concepts. The "advanced" features (&lt;a href="https://dev.java/learn/lambdas/" rel="noopener noreferrer"&gt;lambdas&lt;/a&gt;, &lt;a href="https://dev.java/learn/api/streams/" rel="noopener noreferrer"&gt;streams&lt;/a&gt;, &lt;a href="https://dev.java/learn/records/" rel="noopener noreferrer"&gt;records&lt;/a&gt;, &lt;a href="https://dev.java/learn/pattern-matching/" rel="noopener noreferrer"&gt;pattern matching&lt;/a&gt;) aren't advanced — they're just &lt;em&gt;Java&lt;/em&gt;. They're better defaults that the language now promotes.&lt;/p&gt;

&lt;p&gt;When someone asks me if they should "first complete Core Java before moving to Advanced Java," I tell them they're asking the wrong question. You should learn Java — all of it — progressively, as you need it. The language is one continuous journey, not two separate destinations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Truth #3: What Programming Languages Actually Do
&lt;/h3&gt;

&lt;p&gt;Here's something many beginners miss: &lt;strong&gt;every programming language does the same fundamental thing — it tells a computer what to do.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the end, everything becomes 1s and 0s for your CPU. It doesn't matter what language you use to write your next application. Languages exist for the efficiency and benefit of &lt;em&gt;developers&lt;/em&gt;, not computers.&lt;/p&gt;

&lt;p&gt;Java gives you a particular set of tools and patterns that work exceptionally well for building large-scale, maintainable, enterprise-grade software. That's why it's been among the world's most popular languages for three decades.&lt;/p&gt;

&lt;h3&gt;
  
  
  Truth #4: AI Will Not Replace You (But It Will Change How You Work)
&lt;/h3&gt;

&lt;p&gt;I see the anxiety in my students' eyes when they ask: "Sir, will AI replace programmers?"&lt;/p&gt;

&lt;p&gt;No. And here's why.&lt;/p&gt;

&lt;p&gt;AI is a tool, governed by rules, just like any other tool. A hammer doesn't replace a carpenter. A calculator doesn't replace a mathematician. AI doesn't replace programmers — it amplifies them.&lt;/p&gt;

&lt;p&gt;But here's the catch: &lt;strong&gt;a tool is only as good as the person wielding it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you don't understand what code does, you can't verify if AI-generated code is correct. You can't debug it. You can't improve it. You can't explain it in an interview. You become a "vibe coder" — someone who prompts AI until something works, without understanding &lt;em&gt;why&lt;/em&gt; it works.&lt;/p&gt;

&lt;p&gt;The developers who will thrive are those who use AI to accelerate their work while maintaining deep understanding. Who use AI to learn faster, not to avoid learning altogether.&lt;/p&gt;

&lt;p&gt;That's what this roadmap will teach you to do.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 2026 Transformation: Java's New On-Ramp
&lt;/h2&gt;

&lt;p&gt;Something remarkable happened with &lt;a href="https://dev.java/learn/" rel="noopener noreferrer"&gt;Java 25 LTS&lt;/a&gt; (released September 16, 2025). The language designers finally addressed a problem that educators have complained about for 25 years: the ceremony required to write your first program.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://x.com/briangoetz" rel="noopener noreferrer"&gt;Brian Goetz&lt;/a&gt; (Java's Language Architect at Oracle) and the team designed what they call a &lt;a href="https://dev.java/learn/simple-main-method/" rel="noopener noreferrer"&gt;"smooth on-ramp"&lt;/a&gt; to Java programming.&lt;/p&gt;

&lt;p&gt;The classic "Hello World" in Java used to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWorld&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five lines packed with concepts a beginner can't possibly understand on day one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;public&lt;/code&gt; (twice!) — &lt;a href="https://dev.java/learn/encapsulation/" rel="noopener noreferrer"&gt;access modifiers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;class&lt;/code&gt; — &lt;a href="https://dev.java/learn/oop/" rel="noopener noreferrer"&gt;object-oriented programming&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;static&lt;/code&gt; — a concept that creates bad habits that must be unlearned&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;void&lt;/code&gt; — &lt;a href="https://dev.java/learn/methods/" rel="noopener noreferrer"&gt;return types&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;String[] args&lt;/code&gt; — &lt;a href="https://dev.java/learn/language-basics/arrays/" rel="noopener noreferrer"&gt;arrays&lt;/a&gt; and command-line arguments&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;System.out.println&lt;/code&gt; — three different concepts chained together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, in Java 25, your first program looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No ceremony. No magic incantations. Just logic.&lt;/p&gt;

&lt;p&gt;And here's the beautiful part: when you're ready to learn about classes, access modifiers, and the &lt;code&gt;static&lt;/code&gt; keyword, you simply &lt;em&gt;add&lt;/em&gt; them to what you already know. Nothing needs to be unlearned.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Roadmap: A Computation-First Approach
&lt;/h2&gt;

&lt;p&gt;Based on years of teaching experience and the latest pedagogical research, I recommend what I call a &lt;strong&gt;Computation-First&lt;/strong&gt; approach. Here's the philosophy:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Focus&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phase 0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Environment &amp;amp; Tools&lt;/td&gt;
&lt;td&gt;You can't learn to drive without a car&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phase 1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Computational Thinking&lt;/td&gt;
&lt;td&gt;Focus on logic, not language ceremony&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phase 2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Functions &amp;amp; Methods&lt;/td&gt;
&lt;td&gt;Decompose problems into reusable blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phase 3A&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Core Object-Oriented Thinking&lt;/td&gt;
&lt;td&gt;Model the real world with classes and objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phase 3B&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Advanced OOP Patterns&lt;/td&gt;
&lt;td&gt;Exceptions, threads, generics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phase 4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Modern Java&lt;/td&gt;
&lt;td&gt;Collections, streams, records, virtual threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phase 5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data &amp;amp; Persistence&lt;/td&gt;
&lt;td&gt;Databases, JDBC, JPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phase 6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot 4 &amp;amp; APIs&lt;/td&gt;
&lt;td&gt;Build real backend applications with AI integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Phase 7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Professional Practices&lt;/td&gt;
&lt;td&gt;Testing, Docker, CI/CD, Git workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let me walk you through each phase.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 0: Your Toolkit
&lt;/h3&gt;

&lt;p&gt;Before you write a single line of code, you need your tools ready.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Required:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://jdk.java.net/25/" rel="noopener noreferrer"&gt;Java 25 LTS&lt;/a&gt;&lt;/strong&gt; — Download from the official OpenJDK site or use &lt;a href="https://sdkman.io/" rel="noopener noreferrer"&gt;SDKMAN&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.jetbrains.com/idea/" rel="noopener noreferrer"&gt;IntelliJ IDEA&lt;/a&gt;&lt;/strong&gt; — The 2025.3 release unified Community and Ultimate editions into a single product; core features remain free for both commercial and non-commercial use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;/strong&gt; — Version control is non-negotiable in 2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; Account&lt;/strong&gt; — Your code needs a home&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Reference Book&lt;/strong&gt; — I recommend &lt;a href="https://amzn.in/d/itC6EeE" rel="noopener noreferrer"&gt;"Java: A Beginner's Guide" by Herbert Schildt (9th Edition)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why IntelliJ?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I know &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;VS Code&lt;/a&gt; is popular. If you're on a limited machine and VS Code is your only option, that's fine — I'll sprinkle in guidance for you throughout the reference document.&lt;/p&gt;

&lt;p&gt;But IntelliJ is purpose-built for Java. Its code completion, refactoring tools, and debugger will teach you as you code. With the unified 2025.3 release, you get a single installer with core Java development features free out of the box, plus a 30-day trial of advanced features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Git from Day 1?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because every piece of code you write should be version-controlled. This isn't optional. This isn't "something you'll learn later." From your very first program, you'll be committing, pushing, and building a history of your learning journey.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 1: Computational Foundations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Think like a programmer. Express logic in code.&lt;/p&gt;

&lt;p&gt;Using &lt;a href="https://dev.java/learn/simple-main-method/" rel="noopener noreferrer"&gt;Java 25's simplified syntax&lt;/a&gt;, you'll learn to solve problems before worrying about language ceremony. This phase is about training your brain to break down problems into steps a computer can execute.&lt;/p&gt;

&lt;h4&gt;
  
  
  1.1 Syntax and Semantics
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/numbers-strings/" rel="noopener noreferrer"&gt;Data Types&lt;/a&gt; (The 8 Java Primitive Types: &lt;code&gt;byte&lt;/code&gt;, &lt;code&gt;short&lt;/code&gt;, &lt;code&gt;int&lt;/code&gt;, &lt;code&gt;long&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;double&lt;/code&gt;, &lt;code&gt;char&lt;/code&gt;, &lt;code&gt;boolean&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/language-basics/variables/" rel="noopener noreferrer"&gt;Variables&lt;/a&gt; (declaration, initialization, the &lt;a href="https://dev.java/learn/local-variable-type-inference/" rel="noopener noreferrer"&gt;&lt;code&gt;var&lt;/code&gt; keyword&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/numbers-strings/" rel="noopener noreferrer"&gt;Type Conversion &amp;amp; Casting&lt;/a&gt; (widening, narrowing, explicit casting)&lt;/li&gt;
&lt;li&gt;Automatic Type Promotion in Expressions&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/language-basics/arrays/" rel="noopener noreferrer"&gt;Arrays&lt;/a&gt; (single-dimensional, multi-dimensional, &lt;code&gt;length&lt;/code&gt; property)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/language-basics/operators/" rel="noopener noreferrer"&gt;Operators in Java&lt;/a&gt;: Arithmetic, Bitwise, Relational, Boolean, Assignment, Ternary, and Operator Precedence&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/language-basics/controlling-flow/" rel="noopener noreferrer"&gt;Control Statements&lt;/a&gt;: &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;if-else&lt;/code&gt;, &lt;code&gt;if-else-if&lt;/code&gt; ladder, &lt;a href="https://dev.java/learn/switch-statement/" rel="noopener noreferrer"&gt;&lt;code&gt;switch&lt;/code&gt; statement&lt;/a&gt; (traditional and modern switch expressions)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/language-basics/controlling-flow/" rel="noopener noreferrer"&gt;Iteration Statements&lt;/a&gt;: &lt;code&gt;while&lt;/code&gt;, &lt;code&gt;do-while&lt;/code&gt;, &lt;code&gt;for&lt;/code&gt;, enhanced &lt;code&gt;for-each&lt;/code&gt;, nested loops, &lt;code&gt;break&lt;/code&gt;, &lt;code&gt;continue&lt;/code&gt;, and labeled statements&lt;/li&gt;
&lt;li&gt;Using Command-Line Arguments&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://dev.java/learn/numbers-strings/" rel="noopener noreferrer"&gt;&lt;code&gt;String&lt;/code&gt; class&lt;/a&gt; (creation, immutability, common methods)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/methods/" rel="noopener noreferrer"&gt;Varargs&lt;/a&gt; (Variable Length Arguments)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/reading-writing-files/" rel="noopener noreferrer"&gt;&lt;code&gt;Scanner&lt;/code&gt; class&lt;/a&gt; for input&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/reading-writing-files/" rel="noopener noreferrer"&gt;&lt;code&gt;BufferedReader&lt;/code&gt; class&lt;/a&gt; for efficient input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; At this phase, you're not learning "Java." You're learning to think computationally. The syntax is just how you express those thoughts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt; &lt;a href="https://codingbat.com/java" rel="noopener noreferrer"&gt;CodingBat&lt;/a&gt; — Start with Warmup-1, String-1, Array-1. Don't move forward until you can solve these without looking up syntax.&lt;/p&gt;

&lt;h4&gt;
  
  
  Phase Complete Checkpoint
&lt;/h4&gt;

&lt;p&gt;Before moving to Phase 2, you should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a program that reads input and produces output based on conditions&lt;/li&gt;
&lt;li&gt;Use loops to process collections of data&lt;/li&gt;
&lt;li&gt;Explain why &lt;code&gt;int x = 10/3&lt;/code&gt; gives you &lt;code&gt;3&lt;/code&gt;, not &lt;code&gt;3.333&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Debug a simple program by reading error messages&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Phase 2: Functions &amp;amp; Methods
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Break problems into smaller, reusable pieces.&lt;/p&gt;

&lt;p&gt;Methods are the building blocks of all software. Before you learn about objects, you need to master the art of decomposing problems into functions that do one thing well.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.1 Methods
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/methods/" rel="noopener noreferrer"&gt;Declaring and Using Methods&lt;/a&gt; (method signature, method body)&lt;/li&gt;
&lt;li&gt;Method Return Types (&lt;code&gt;void&lt;/code&gt; and value-returning methods)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/methods/" rel="noopener noreferrer"&gt;Method Parameters and Arguments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Pass by Value semantics in Java&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/methods/" rel="noopener noreferrer"&gt;Method Overloading&lt;/a&gt; (same name, different parameters)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/methods/" rel="noopener noreferrer"&gt;Recursion&lt;/a&gt; (base case, recursive case, call stack, tail recursion, head recursion)&lt;/li&gt;
&lt;li&gt;Variable Scope (local variables, block scope, shadowing)&lt;/li&gt;
&lt;li&gt;The Call Stack and Stack Frames&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this before OOP?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because understanding methods is foundational. A class is just a container for related methods and data. If you don't understand methods, classes will feel like magic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt; Complete &lt;a href="https://codingbat.com/java" rel="noopener noreferrer"&gt;CodingBat&lt;/a&gt; Logic-1, Logic-2, String-2. Then move to &lt;a href="https://exercism.org/tracks/java" rel="noopener noreferrer"&gt;Exercism Java Track&lt;/a&gt; for more substantial exercises.&lt;/p&gt;

&lt;h4&gt;
  
  
  Phase Complete Checkpoint
&lt;/h4&gt;

&lt;p&gt;Before moving to Phase 3A, you should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract repeated code into a method&lt;/li&gt;
&lt;li&gt;Write a recursive function to solve a simple problem (like factorial or Fibonacci)&lt;/li&gt;
&lt;li&gt;Explain what the call stack is and why it matters&lt;/li&gt;
&lt;li&gt;Overload a method with different parameter types&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Phase 3A: Core Object-Oriented Programming
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Learn to model the real world using classes and objects.&lt;/p&gt;

&lt;p&gt;You've been writing programs as sequences of instructions. Now comes the paradigm shift: instead of &lt;em&gt;doing things&lt;/em&gt;, you'll define &lt;em&gt;things that do&lt;/em&gt;. A &lt;code&gt;Citizen&lt;/code&gt; isn't a list of variables—it's an entity with identity, state, and behavior.&lt;/p&gt;

&lt;p&gt;This phase builds the mental model that powers enterprise Java. Take your time here.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.1 Classes and Objects
&lt;/h4&gt;

&lt;p&gt;Every Java program you've written has secretly been using classes. Now you'll create your own.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/oop/" rel="noopener noreferrer"&gt;Types of Program Units in Java&lt;/a&gt; (Class, Abstract Class, Interface, Enum, &lt;a href="https://dev.java/learn/records/" rel="noopener noreferrer"&gt;Record&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;Declaring Classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;Creating Objects&lt;/a&gt; (the &lt;code&gt;new&lt;/code&gt; keyword, object references)&lt;/li&gt;
&lt;li&gt;Reference Types vs Primitive Types&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;Instance Variables (Fields)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/methods/" rel="noopener noreferrer"&gt;Methods&lt;/a&gt; (instance methods, method signatures)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;Constructors&lt;/a&gt; (default constructor, parameterized constructors)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;The &lt;code&gt;this&lt;/code&gt; keyword&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;Overloading Constructors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Using Objects as Method Parameters and Return Types&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;The &lt;code&gt;static&lt;/code&gt; keyword&lt;/a&gt; (static fields, static methods, static blocks)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;The &lt;code&gt;final&lt;/code&gt; keyword&lt;/a&gt; (final classes, final methods, final variables/constants)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;Nested Classes and Inner Classes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.2 Inheritance
&lt;/h4&gt;

&lt;p&gt;Inheritance lets you build on existing code without rewriting it. It's one of the most powerful tools for code reuse—but also one of the most misused.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/inheritance/" rel="noopener noreferrer"&gt;Inheritance in Java&lt;/a&gt; (&lt;code&gt;extends&lt;/code&gt; keyword)&lt;/li&gt;
&lt;li&gt;Superclass and Subclass relationships&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/inheritance/" rel="noopener noreferrer"&gt;The &lt;code&gt;super&lt;/code&gt; keyword&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Types of Inheritance Hierarchies (single, multilevel)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/inheritance/" rel="noopener noreferrer"&gt;Method Overriding&lt;/a&gt; and the &lt;code&gt;@Override&lt;/code&gt; annotation&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/inheritance/" rel="noopener noreferrer"&gt;Polymorphism&lt;/a&gt; (runtime polymorphism, dynamic method dispatch)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.3 Abstract Classes and Interfaces
&lt;/h4&gt;

&lt;p&gt;These define contracts that classes must fulfill. They're the foundation of flexible, maintainable architecture.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/interfaces/" rel="noopener noreferrer"&gt;Abstract Classes&lt;/a&gt; (abstract methods, concrete methods)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/interfaces/" rel="noopener noreferrer"&gt;Interfaces&lt;/a&gt; (defining, implementing, multiple inheritance of type)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/interfaces/" rel="noopener noreferrer"&gt;Default Methods in Interfaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Static Methods in Interfaces&lt;/li&gt;
&lt;li&gt;Private Methods in Interfaces (Java 9+)&lt;/li&gt;
&lt;li&gt;Functional Interfaces (single abstract method)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.4 Packages and Access Control
&lt;/h4&gt;

&lt;p&gt;Encapsulation is about hiding complexity. Packages and access modifiers are your tools for building clean, maintainable systems.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/packages/" rel="noopener noreferrer"&gt;Packages&lt;/a&gt; (creating, naming conventions, &lt;code&gt;CLASSPATH&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/packages/" rel="noopener noreferrer"&gt;Importing Packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/encapsulation/" rel="noopener noreferrer"&gt;Access Modifiers&lt;/a&gt; (&lt;code&gt;public&lt;/code&gt;, &lt;code&gt;protected&lt;/code&gt;, default/package-private, &lt;code&gt;private&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Encapsulation and Information Hiding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt; Complete Exercism's "Classes" and "Inheritance" exercises in the Java track.&lt;/p&gt;

&lt;h4&gt;
  
  
  Phase Complete Checkpoint
&lt;/h4&gt;

&lt;p&gt;Before moving to Phase 3B, you should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a class with private fields, a constructor, and getter methods&lt;/li&gt;
&lt;li&gt;Explain why we hide data behind methods (not just "because OOP says so")&lt;/li&gt;
&lt;li&gt;Write a simple inheritance hierarchy without looking up syntax&lt;/li&gt;
&lt;li&gt;Implement an interface and explain when to use interface vs abstract class&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Phase 3B: Advanced OOP Patterns
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Master the constructs that enable large-scale, maintainable code.&lt;/p&gt;

&lt;p&gt;Core OOP taught you to think in objects. This phase teaches you to think in &lt;em&gt;systems of objects&lt;/em&gt;—how they communicate, what they guarantee to each other, and how they fail gracefully.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.5 Exception Handling
&lt;/h4&gt;

&lt;p&gt;When something goes wrong (and it will), you need a plan. Exception handling is how Java programs deal with errors gracefully.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/exceptions/" rel="noopener noreferrer"&gt;Exception Types&lt;/a&gt; (checked vs unchecked, &lt;code&gt;Error&lt;/code&gt; vs &lt;code&gt;Exception&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/exceptions/" rel="noopener noreferrer"&gt;The Exception Hierarchy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/exceptions/" rel="noopener noreferrer"&gt;&lt;code&gt;try-catch&lt;/code&gt; blocks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/exceptions/" rel="noopener noreferrer"&gt;&lt;code&gt;finally&lt;/code&gt; block&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/exceptions/" rel="noopener noreferrer"&gt;try-with-resources&lt;/a&gt; (automatic resource management)&lt;/li&gt;
&lt;li&gt;Multiple catch blocks and multi-catch&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/exceptions/" rel="noopener noreferrer"&gt;&lt;code&gt;throw&lt;/code&gt; and &lt;code&gt;throws&lt;/code&gt;&lt;/a&gt; keywords&lt;/li&gt;
&lt;li&gt;Creating Custom Exceptions&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.6 Multithreaded Programming Basics
&lt;/h4&gt;

&lt;p&gt;Concurrency is how programs do multiple things at once. Understanding the basics prepares you for modern concurrency in Phase 4.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/java-concurrency-utilities/" rel="noopener noreferrer"&gt;The Java Thread Model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The Main Thread&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/java-concurrency-utilities/" rel="noopener noreferrer"&gt;Creating Threads&lt;/a&gt; (&lt;code&gt;Thread&lt;/code&gt; class, &lt;code&gt;Runnable&lt;/code&gt; interface)&lt;/li&gt;
&lt;li&gt;Thread Lifecycle and States&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sleep()&lt;/code&gt;, &lt;code&gt;join()&lt;/code&gt;, thread priorities&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/java-concurrency-utilities/" rel="noopener noreferrer"&gt;Synchronization basics&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.7 Enumerations and Annotations
&lt;/h4&gt;

&lt;p&gt;Enums give you type-safe constants. Annotations let you add metadata to your code that tools can process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/classes-objects/" rel="noopener noreferrer"&gt;Enum Types&lt;/a&gt; (declaration, methods, constructors)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/numbers-strings/" rel="noopener noreferrer"&gt;Type Wrappers&lt;/a&gt; (&lt;code&gt;Integer&lt;/code&gt;, &lt;code&gt;Double&lt;/code&gt;, &lt;code&gt;Boolean&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/numbers-strings/" rel="noopener noreferrer"&gt;Autoboxing and Unboxing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/annotations/" rel="noopener noreferrer"&gt;Annotations&lt;/a&gt; (&lt;code&gt;@Override&lt;/code&gt;, &lt;code&gt;@Deprecated&lt;/code&gt;, &lt;code&gt;@SuppressWarnings&lt;/code&gt;, custom annotations)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3.8 Generics
&lt;/h4&gt;

&lt;p&gt;Generics let you write code that works with any type while maintaining type safety. They're essential for understanding collections and modern Java APIs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/generics/" rel="noopener noreferrer"&gt;Introduction to Generics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/generics/" rel="noopener noreferrer"&gt;Generic Classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/generics/" rel="noopener noreferrer"&gt;Generic Methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/generics/" rel="noopener noreferrer"&gt;Bounded Type Parameters&lt;/a&gt; (&lt;code&gt;extends&lt;/code&gt;, &lt;code&gt;super&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/generics/" rel="noopener noreferrer"&gt;Wildcards&lt;/a&gt; (&lt;code&gt;?&lt;/code&gt;, &lt;code&gt;? extends T&lt;/code&gt;, &lt;code&gt;? super T&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Type Erasure&lt;/li&gt;
&lt;li&gt;Generic Constructors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt; Complete Exercism's "Generics" and "Exception Handling" exercises.&lt;/p&gt;

&lt;h4&gt;
  
  
  Phase Complete Checkpoint
&lt;/h4&gt;

&lt;p&gt;Before moving to Phase 4, you should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handle an exception you intentionally throw&lt;/li&gt;
&lt;li&gt;Create a generic class that works with different types&lt;/li&gt;
&lt;li&gt;Explain the difference between checked and unchecked exceptions&lt;/li&gt;
&lt;li&gt;Create a simple two-thread program that shares data safely&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Phase 4: Modern Java
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Write idiomatic, modern Java code using the language's latest features.&lt;/p&gt;

&lt;p&gt;This phase covers the features that distinguish modern Java from "your father's Java." These aren't advanced—they're the new baseline.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.1 Collections Framework
&lt;/h4&gt;

&lt;p&gt;Collections are the containers you use every day. You must understand these &lt;strong&gt;before&lt;/strong&gt; learning Streams, because streams operate on collections.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/api/collections-framework/" rel="noopener noreferrer"&gt;Collection Interfaces&lt;/a&gt;: &lt;a href="https://dev.java/learn/api/collections-framework/" rel="noopener noreferrer"&gt;&lt;code&gt;List&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/api/collections-framework/" rel="noopener noreferrer"&gt;&lt;code&gt;Set&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/api/collections-framework/" rel="noopener noreferrer"&gt;&lt;code&gt;Queue&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/api/collections-framework/" rel="noopener noreferrer"&gt;&lt;code&gt;Deque&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/api/collections-framework/" rel="noopener noreferrer"&gt;&lt;code&gt;Map&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/api/collections-framework/" rel="noopener noreferrer"&gt;Collection Classes&lt;/a&gt;: &lt;code&gt;ArrayList&lt;/code&gt;, &lt;code&gt;LinkedList&lt;/code&gt;, &lt;code&gt;HashSet&lt;/code&gt;, &lt;code&gt;TreeSet&lt;/code&gt;, &lt;code&gt;HashMap&lt;/code&gt;, &lt;code&gt;TreeMap&lt;/code&gt;, &lt;code&gt;PriorityQueue&lt;/code&gt;, &lt;code&gt;ArrayDeque&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/api/collections-framework/" rel="noopener noreferrer"&gt;Iterators&lt;/a&gt; and &lt;code&gt;ListIterator&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Immutable Collections (&lt;code&gt;List.of()&lt;/code&gt;, &lt;code&gt;Set.of()&lt;/code&gt;, &lt;code&gt;Map.of()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Collections before Streams?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because you can't understand &lt;code&gt;list.stream().filter(...).collect(...)&lt;/code&gt; if you don't understand what a &lt;code&gt;List&lt;/code&gt; is. Streams operate &lt;em&gt;on&lt;/em&gt; collections. Learn the containers first.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.2 Records and Sealed Classes
&lt;/h4&gt;

&lt;p&gt;Records eliminate boilerplate for data classes. Sealed classes give you control over inheritance hierarchies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/records/" rel="noopener noreferrer"&gt;Records&lt;/a&gt; (compact constructors, canonical constructors, custom methods)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/sealed-classes/" rel="noopener noreferrer"&gt;Sealed Classes&lt;/a&gt; (&lt;code&gt;sealed&lt;/code&gt;, &lt;code&gt;permits&lt;/code&gt;, &lt;code&gt;non-sealed&lt;/code&gt;, &lt;code&gt;final&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4.3 Pattern Matching
&lt;/h4&gt;

&lt;p&gt;Pattern matching makes your code more expressive and eliminates many explicit casts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/pattern-matching/" rel="noopener noreferrer"&gt;&lt;code&gt;instanceof&lt;/code&gt; Pattern Matching&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/pattern-matching/" rel="noopener noreferrer"&gt;Pattern Matching in &lt;code&gt;switch&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Record Patterns&lt;/li&gt;
&lt;li&gt;Guarded Patterns (&lt;code&gt;when&lt;/code&gt; clause)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4.4 Text Blocks and String Enhancements
&lt;/h4&gt;

&lt;p&gt;Multi-line strings are finally readable in Java.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/text-blocks/" rel="noopener noreferrer"&gt;Text Blocks&lt;/a&gt; (multi-line strings)&lt;/li&gt;
&lt;li&gt;String &lt;code&gt;formatted()&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;String &lt;code&gt;indent()&lt;/code&gt;, &lt;code&gt;stripIndent()&lt;/code&gt;, &lt;code&gt;translateEscapes()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4.5 Lambda Expressions
&lt;/h4&gt;

&lt;p&gt;Lambdas let you treat functions as values. They're the foundation of functional programming in Java.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/lambdas/" rel="noopener noreferrer"&gt;Lambda Expression Syntax&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/lambdas/" rel="noopener noreferrer"&gt;Functional Interfaces&lt;/a&gt; (&lt;code&gt;@FunctionalInterface&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Built-in Functional Interfaces: &lt;a href="https://dev.java/learn/api/standard-functional-interfaces/" rel="noopener noreferrer"&gt;&lt;code&gt;Predicate&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/api/standard-functional-interfaces/" rel="noopener noreferrer"&gt;&lt;code&gt;Function&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/api/standard-functional-interfaces/" rel="noopener noreferrer"&gt;&lt;code&gt;Consumer&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/api/standard-functional-interfaces/" rel="noopener noreferrer"&gt;&lt;code&gt;Supplier&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/api/standard-functional-interfaces/" rel="noopener noreferrer"&gt;&lt;code&gt;BiFunction&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/lambdas/" rel="noopener noreferrer"&gt;Method References&lt;/a&gt; (&lt;code&gt;::&lt;/code&gt; operator)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4.6 Stream API
&lt;/h4&gt;

&lt;p&gt;Streams let you process collections declaratively. They're one of the most powerful features in modern Java.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/api/streams/" rel="noopener noreferrer"&gt;Introduction to Streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/api/streams/" rel="noopener noreferrer"&gt;Creating Streams&lt;/a&gt; (&lt;code&gt;stream()&lt;/code&gt;, &lt;code&gt;of()&lt;/code&gt;, &lt;code&gt;generate()&lt;/code&gt;, &lt;code&gt;iterate()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Intermediate Operations (&lt;code&gt;filter()&lt;/code&gt;, &lt;code&gt;map()&lt;/code&gt;, &lt;code&gt;flatMap()&lt;/code&gt;, &lt;code&gt;sorted()&lt;/code&gt;, &lt;code&gt;distinct()&lt;/code&gt;, &lt;code&gt;limit()&lt;/code&gt;, &lt;code&gt;skip()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Terminal Operations (&lt;code&gt;forEach()&lt;/code&gt;, &lt;code&gt;collect()&lt;/code&gt;, &lt;code&gt;reduce()&lt;/code&gt;, &lt;code&gt;count()&lt;/code&gt;, &lt;code&gt;findFirst()&lt;/code&gt;, &lt;code&gt;findAny()&lt;/code&gt;, &lt;code&gt;anyMatch()&lt;/code&gt;, &lt;code&gt;allMatch()&lt;/code&gt;, &lt;code&gt;noneMatch()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/api/streams/" rel="noopener noreferrer"&gt;Collectors&lt;/a&gt; (&lt;code&gt;toList()&lt;/code&gt;, &lt;code&gt;toSet()&lt;/code&gt;, &lt;code&gt;toMap()&lt;/code&gt;, &lt;code&gt;groupingBy()&lt;/code&gt;, &lt;code&gt;partitioningBy()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Parallel Streams&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4.7 Optional
&lt;/h4&gt;

&lt;p&gt;Optional helps you handle null values safely and expressively.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/api/optional/" rel="noopener noreferrer"&gt;The &lt;code&gt;Optional&lt;/code&gt; class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Creating Optionals (&lt;code&gt;of()&lt;/code&gt;, &lt;code&gt;ofNullable()&lt;/code&gt;, &lt;code&gt;empty()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Checking and Retrieving (&lt;code&gt;isPresent()&lt;/code&gt;, &lt;code&gt;isEmpty()&lt;/code&gt;, &lt;code&gt;get()&lt;/code&gt;, &lt;code&gt;orElse()&lt;/code&gt;, &lt;code&gt;orElseGet()&lt;/code&gt;, &lt;code&gt;orElseThrow()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Transforming (&lt;code&gt;map()&lt;/code&gt;, &lt;code&gt;flatMap()&lt;/code&gt;, &lt;code&gt;filter()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4.8 Modern Concurrency (Java 21+)
&lt;/h4&gt;

&lt;p&gt;Java's concurrency model has evolved dramatically. Virtual threads change everything about how we think about concurrent programming.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/virtual-threads/" rel="noopener noreferrer"&gt;Virtual Threads&lt;/a&gt; (stable since Java 21, Project Loom)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.org/jeps/505" rel="noopener noreferrer"&gt;Structured Concurrency&lt;/a&gt; (&lt;code&gt;StructuredTaskScope&lt;/code&gt;) — &lt;em&gt;Note: Still in preview as of Java 25 (JEP 505)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://openjdk.org/jeps/506" rel="noopener noreferrer"&gt;Scoped Values&lt;/a&gt; (finalized in Java 25, replacement for ThreadLocal)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/java-concurrency-utilities/" rel="noopener noreferrer"&gt;&lt;code&gt;CompletableFuture&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4.9 Date-Time API
&lt;/h4&gt;

&lt;p&gt;The modern Date-Time API is immutable, thread-safe, and actually makes sense.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/date-time/" rel="noopener noreferrer"&gt;&lt;code&gt;LocalDate&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/date-time/" rel="noopener noreferrer"&gt;&lt;code&gt;LocalTime&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://dev.java/learn/date-time/" rel="noopener noreferrer"&gt;&lt;code&gt;LocalDateTime&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/date-time/" rel="noopener noreferrer"&gt;&lt;code&gt;ZonedDateTime&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/date-time/" rel="noopener noreferrer"&gt;&lt;code&gt;Duration&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://dev.java/learn/date-time/" rel="noopener noreferrer"&gt;&lt;code&gt;Period&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/date-time/" rel="noopener noreferrer"&gt;&lt;code&gt;DateTimeFormatter&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt; Complete Exercism's "Streams", "Optionals", and "Date-Time" exercises.&lt;/p&gt;

&lt;h4&gt;
  
  
  Phase Complete Checkpoint
&lt;/h4&gt;

&lt;p&gt;Before moving to Phase 5, you should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use streams to filter, transform, and collect data&lt;/li&gt;
&lt;li&gt;Create a record class and explain its advantages over a regular class&lt;/li&gt;
&lt;li&gt;Use Optional to handle potentially null values&lt;/li&gt;
&lt;li&gt;Explain when to use virtual threads vs traditional threads&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Phase 5: Data &amp;amp; Persistence
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Store and retrieve data professionally.&lt;/p&gt;

&lt;p&gt;A Java developer who can't work with databases is unemployable in 2026. This phase is not optional.&lt;/p&gt;

&lt;h4&gt;
  
  
  5.1 SQL Fundamentals
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;DDL: &lt;code&gt;CREATE&lt;/code&gt;, &lt;code&gt;ALTER&lt;/code&gt;, &lt;code&gt;DROP&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;DML: &lt;code&gt;SELECT&lt;/code&gt;, &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;ORDER BY&lt;/code&gt;, &lt;code&gt;GROUP BY&lt;/code&gt;, &lt;code&gt;HAVING&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;JOIN&lt;/code&gt; operations (INNER, LEFT, RIGHT, FULL)&lt;/li&gt;
&lt;li&gt;Aggregate functions (&lt;code&gt;COUNT&lt;/code&gt;, &lt;code&gt;SUM&lt;/code&gt;, &lt;code&gt;AVG&lt;/code&gt;, &lt;code&gt;MIN&lt;/code&gt;, &lt;code&gt;MAX&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Subqueries and Common Table Expressions (CTEs)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5.2 JDBC
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.java/learn/jdbc/" rel="noopener noreferrer"&gt;JDBC Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DriverManager&lt;/code&gt; and database connections&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Connection&lt;/code&gt;, &lt;code&gt;Statement&lt;/code&gt;, &lt;code&gt;PreparedStatement&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ResultSet&lt;/code&gt; processing&lt;/li&gt;
&lt;li&gt;Transaction management&lt;/li&gt;
&lt;li&gt;Connection Pooling with &lt;a href="https://github.com/brettwooldridge/HikariCP" rel="noopener noreferrer"&gt;HikariCP&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5.3 JPA/Hibernate
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jakarta.ee/specifications/persistence/" rel="noopener noreferrer"&gt;Jakarta Persistence (JPA)&lt;/a&gt; concepts&lt;/li&gt;
&lt;li&gt;Entity mapping (&lt;code&gt;@Entity&lt;/code&gt;, &lt;code&gt;@Table&lt;/code&gt;, &lt;code&gt;@Id&lt;/code&gt;, &lt;code&gt;@GeneratedValue&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Field mapping (&lt;code&gt;@Column&lt;/code&gt;, &lt;code&gt;@Transient&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Relationships (&lt;code&gt;@OneToOne&lt;/code&gt;, &lt;code&gt;@OneToMany&lt;/code&gt;, &lt;code&gt;@ManyToOne&lt;/code&gt;, &lt;code&gt;@ManyToMany&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;JPQL (Java Persistence Query Language)&lt;/li&gt;
&lt;li&gt;Criteria API&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  5.4 Spring Data JPA
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-data/jpa/reference/index.html" rel="noopener noreferrer"&gt;Spring Data JPA Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Repository pattern (&lt;code&gt;JpaRepository&lt;/code&gt;, &lt;code&gt;CrudRepository&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Query methods (method name derivation)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Query&lt;/code&gt; annotation&lt;/li&gt;
&lt;li&gt;Pagination and Sorting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt; Build a data access layer for your JanSahayak project using Spring Data JPA.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 6: Spring Boot 4 &amp;amp; Beyond
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Build production-grade applications with the latest Spring ecosystem.&lt;/p&gt;

&lt;h4&gt;
  
  
  6.1 Spring Boot 4 Fundamentals
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://spring.io/blog/2025/11/20/spring-boot-4-0-0-available-now" rel="noopener noreferrer"&gt;Spring Boot 4.0&lt;/a&gt; was released on November 20, 2025 and is built on &lt;a href="https://github.com/spring-projects/spring-framework/releases/tag/v7.0.0" rel="noopener noreferrer"&gt;Spring Framework 7&lt;/a&gt;. Key features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://spring.io/blog/2025/10/28/modularizing-spring-boot" rel="noopener noreferrer"&gt;Modular codebase&lt;/a&gt; — smaller, more focused jars&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://spring.io/blog/2025/11/12/null-safe-applications-with-spring-boot-4" rel="noopener noreferrer"&gt;Null safety with JSpecify&lt;/a&gt; — portfolio-wide improvements&lt;/li&gt;
&lt;li&gt;First-class Java 25 support (while retaining Java 17 compatibility)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://spring.io/blog/2025/09/16/api-versioning-in-spring" rel="noopener noreferrer"&gt;API Versioning&lt;/a&gt; support out of the box&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://spring.io/blog/2025/09/23/http-service-client-enhancements" rel="noopener noreferrer"&gt;HTTP Service Clients&lt;/a&gt; — annotate interfaces, get implementations
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@HttpExchange&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.example.com"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GetExchange&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="nf"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@PostExchange&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;CreateUserRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6.2 Spring Boot 4 New Features
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RestTestClient&lt;/strong&gt;: New testing support for REST APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenTelemetry Starter&lt;/strong&gt;: &lt;code&gt;spring-boot-starter-opentelemetry&lt;/code&gt; for metrics and traces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin Serialization&lt;/strong&gt;: First-class support with &lt;code&gt;spring-boot-kotlin-serialization-starter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis Observability&lt;/strong&gt;: Auto-configured &lt;code&gt;MicrometerTracing&lt;/code&gt; for Redis operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual Thread Integration&lt;/strong&gt;: HTTP clients auto-configured for virtual threads when enabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gradle 9 Support&lt;/strong&gt;: Build with the latest Gradle&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  6.3 Major Dependency Upgrades in Spring Boot 4
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dependency&lt;/th&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spring Framework&lt;/td&gt;
&lt;td&gt;7.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spring Security&lt;/td&gt;
&lt;td&gt;7.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spring Data&lt;/td&gt;
&lt;td&gt;2025.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hibernate&lt;/td&gt;
&lt;td&gt;7.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jackson&lt;/td&gt;
&lt;td&gt;3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tomcat&lt;/td&gt;
&lt;td&gt;11.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jakarta Persistence&lt;/td&gt;
&lt;td&gt;3.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JUnit&lt;/td&gt;
&lt;td&gt;6.x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testcontainers&lt;/td&gt;
&lt;td&gt;2.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  6.4 REST API Development
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html" rel="noopener noreferrer"&gt;&lt;code&gt;@RestController&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Request mapping (&lt;code&gt;@GetMapping&lt;/code&gt;, &lt;code&gt;@PostMapping&lt;/code&gt;, &lt;code&gt;@PutMapping&lt;/code&gt;, &lt;code&gt;@DeleteMapping&lt;/code&gt;, &lt;code&gt;@PatchMapping&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@PathVariable&lt;/code&gt;, &lt;code&gt;@RequestParam&lt;/code&gt;, &lt;code&gt;@RequestBody&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ResponseEntity&lt;/code&gt; for HTTP responses&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.spring.io/spring-boot/4.0/reference/web/servlet.html#web.servlet.spring-mvc.api-versioning" rel="noopener noreferrer"&gt;API Versioning&lt;/a&gt; with &lt;code&gt;spring.mvc.apiversion.*&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  6.5 Spring Security 7.0
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-security/reference/index.html" rel="noopener noreferrer"&gt;Spring Security Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Authentication vs Authorization&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SecurityFilterChain&lt;/code&gt; configuration&lt;/li&gt;
&lt;li&gt;Password encoding with &lt;code&gt;BCryptPasswordEncoder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;JWT (JSON Web Tokens) basics&lt;/li&gt;
&lt;li&gt;OAuth 2.0 integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  6.6 Spring AI (Mandatory in 2026)
&lt;/h4&gt;

&lt;p&gt;AI integration is no longer optional. Spring AI makes it straightforward.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-ai/reference/" rel="noopener noreferrer"&gt;Spring AI Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ChatClient&lt;/code&gt; for LLM interaction&lt;/li&gt;
&lt;li&gt;Prompt engineering&lt;/li&gt;
&lt;li&gt;Structured output (entity extraction)&lt;/li&gt;
&lt;li&gt;RAG (Retrieval-Augmented Generation) basics&lt;/li&gt;
&lt;li&gt;Vector stores integration (Pinecone, pgvector, Milvus)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AIAssistantService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ChatClient&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;AIAssistantService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ChatClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;chatClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;explainScheme&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;schemeName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chatClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Explain the "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;schemeName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" scheme in simple terms"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;call&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt; Add AI-powered features to your JanSahayak project.&lt;/p&gt;




&lt;h3&gt;
  
  
  Phase 7: Professional Practices
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; Work like a professional developer.&lt;/p&gt;

&lt;h4&gt;
  
  
  7.1 Testing
&lt;/h4&gt;

&lt;p&gt;Testing is not optional. JUnit 6 is the default in Spring Boot 4.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://junit.org/junit5/docs/current/user-guide/" rel="noopener noreferrer"&gt;JUnit 6&lt;/a&gt; (&lt;code&gt;@Test&lt;/code&gt;, &lt;code&gt;@BeforeEach&lt;/code&gt;, &lt;code&gt;@AfterEach&lt;/code&gt;, &lt;code&gt;@DisplayName&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Assertions (&lt;code&gt;assertEquals&lt;/code&gt;, &lt;code&gt;assertTrue&lt;/code&gt;, &lt;code&gt;assertThrows&lt;/code&gt;, &lt;code&gt;assertAll&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@ParameterizedTest&lt;/code&gt; and data sources&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://site.mockito.org/" rel="noopener noreferrer"&gt;Mockito&lt;/a&gt; for mocking (&lt;code&gt;@Mock&lt;/code&gt;, &lt;code&gt;@InjectMocks&lt;/code&gt;, &lt;code&gt;when()&lt;/code&gt;, &lt;code&gt;verify()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing" rel="noopener noreferrer"&gt;Spring Boot Testing&lt;/a&gt; (&lt;code&gt;@SpringBootTest&lt;/code&gt;, &lt;code&gt;@WebMvcTest&lt;/code&gt;, &lt;code&gt;@DataJpaTest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.spring.io/spring-framework/reference/testing/resttestclient.html" rel="noopener noreferrer"&gt;RestTestClient&lt;/a&gt; (new in Spring Boot 4)&lt;/li&gt;
&lt;li&gt;Test-Driven Development (TDD) methodology&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  7.2 Build Tools
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://maven.apache.org/guides/" rel="noopener noreferrer"&gt;Maven&lt;/a&gt; (&lt;code&gt;pom.xml&lt;/code&gt;, lifecycle phases, plugins)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.gradle.org/current/userguide/userguide.html" rel="noopener noreferrer"&gt;Gradle 9&lt;/a&gt; (&lt;code&gt;build.gradle&lt;/code&gt;, tasks, plugins)&lt;/li&gt;
&lt;li&gt;Dependency management&lt;/li&gt;
&lt;li&gt;Multi-module projects&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  7.3 Containerization
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; fundamentals (images, containers, layers)&lt;/li&gt;
&lt;li&gt;Writing &lt;code&gt;Dockerfile&lt;/code&gt; (multi-stage builds)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; for multi-container applications&lt;/li&gt;
&lt;li&gt;Container registries&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  7.4 Observability
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://opentelemetry.io/" rel="noopener noreferrer"&gt;OpenTelemetry&lt;/a&gt; with &lt;code&gt;spring-boot-starter-opentelemetry&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Metrics with Micrometer&lt;/li&gt;
&lt;li&gt;Distributed tracing&lt;/li&gt;
&lt;li&gt;Log aggregation&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  7.5 CI/CD
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; workflows&lt;/li&gt;
&lt;li&gt;Automated testing in pipelines&lt;/li&gt;
&lt;li&gt;Build and artifact generation&lt;/li&gt;
&lt;li&gt;Deployment automation&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  7.6 Git Workflows
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://git-scm.com/doc" rel="noopener noreferrer"&gt;Git&lt;/a&gt; branching strategies&lt;/li&gt;
&lt;li&gt;Pull request workflows&lt;/li&gt;
&lt;li&gt;Code review best practices&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.conventionalcommits.org/" rel="noopener noreferrer"&gt;Conventional Commits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.gitignore&lt;/code&gt; configuration&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  For Those Who "Already Know Java"
&lt;/h2&gt;

&lt;p&gt;Maybe you've taken a Java course in college. Maybe you've watched tutorials and built small projects. Maybe you can write code that compiles and runs.&lt;/p&gt;

&lt;p&gt;But can you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explain why we use encapsulation (without just saying "it's a principle of OOP")?&lt;/li&gt;
&lt;li&gt;Write a &lt;a href="https://spring.io/projects/spring-boot" rel="noopener noreferrer"&gt;Spring Boot 4&lt;/a&gt; REST API from scratch without copying code?&lt;/li&gt;
&lt;li&gt;Debug a &lt;code&gt;NullPointerException&lt;/code&gt; by reading the stack trace, not by adding print statements everywhere?&lt;/li&gt;
&lt;li&gt;Write unit tests &lt;em&gt;before&lt;/em&gt; writing the code?&lt;/li&gt;
&lt;li&gt;Set up a CI/CD pipeline that automatically tests and deploys your application?&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://dev.java/learn/virtual-threads/" rel="noopener noreferrer"&gt;Virtual Threads&lt;/a&gt; appropriately in your applications?&lt;/li&gt;
&lt;li&gt;Integrate AI capabilities using &lt;a href="https://spring.io/projects/spring-ai" rel="noopener noreferrer"&gt;Spring AI&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;Explain what happens when you type &lt;code&gt;java MyApp&lt;/code&gt; and press Enter?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you hesitated on any of these, this roadmap is still for you. Use it to identify your gaps. Skip what you truly know. Double down on what you don't.&lt;/p&gt;




&lt;h2&gt;
  
  
  Soft Skills Matter
&lt;/h2&gt;

&lt;p&gt;Technical skills get you interviews. Soft skills get you jobs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commit Messages:&lt;/strong&gt; Follow &lt;a href="https://www.conventionalcommits.org/" rel="noopener noreferrer"&gt;Conventional Commits&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat: add document validation endpoint
fix: handle null scheme descriptions
docs: update API documentation
refactor: extract validation logic to service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Code Reviews:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be kind. The person wrote this code trying their best.&lt;/li&gt;
&lt;li&gt;Be specific. "This could be better" is useless feedback.&lt;/li&gt;
&lt;li&gt;Ask questions instead of making demands. "What was the thinking behind this approach?" beats "This is wrong."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Technical Communication:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write README files that actually help people use your code.&lt;/li&gt;
&lt;li&gt;Document your APIs so others don't have to guess.&lt;/li&gt;
&lt;li&gt;When asking for help, provide context: what you tried, what you expected, what actually happened.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Path Forward
&lt;/h2&gt;

&lt;p&gt;Here's what I want you to do right now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Set up your environment&lt;/strong&gt; — &lt;a href="https://jdk.java.net/25/" rel="noopener noreferrer"&gt;Java 25&lt;/a&gt;, &lt;a href="https://www.jetbrains.com/idea/" rel="noopener noreferrer"&gt;IntelliJ IDEA&lt;/a&gt;, &lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt;, &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a repository&lt;/strong&gt; — Call it &lt;code&gt;java-learning-journey&lt;/code&gt; or whatever speaks to you&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write your first program&lt;/strong&gt; — Use Java 25's simplified syntax&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit it&lt;/strong&gt; — Your journey has officially begun&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, follow the roadmap. Phase by phase. Don't skip ahead because something looks "too basic." Don't rush because you're anxious about the job market.&lt;/p&gt;

&lt;p&gt;The developers who struggle most aren't those who learn slowly — they're those who build on shaky foundations.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Final Word
&lt;/h2&gt;

&lt;p&gt;The job market is brutal right now. I won't pretend otherwise. The days when knowing Java basics could land you an interview are long gone.&lt;/p&gt;

&lt;p&gt;But here's what hasn't changed: &lt;strong&gt;companies still need good developers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They need people who can solve problems, write maintainable code, work in teams, and learn continuously. This roadmap won't make you a developer in 30 days. It won't guarantee you a job. Nothing can.&lt;/p&gt;

&lt;p&gt;What it will do is give you the skills, practices, and portfolio that make you &lt;em&gt;genuinely&lt;/em&gt; valuable — not just someone who can solve LeetCode problems, but someone who can build real software that solves real problems.&lt;/p&gt;

&lt;p&gt;That's worth more than any shortcut.&lt;/p&gt;

&lt;p&gt;Now go build something.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Official Java Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/" rel="noopener noreferrer"&gt;Dev.java - Learn Java&lt;/a&gt; — &lt;strong&gt;Primary learning resource&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/java/" rel="noopener noreferrer"&gt;Oracle Java Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openjdk.org/" rel="noopener noreferrer"&gt;OpenJDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jdk.java.net/25/" rel="noopener noreferrer"&gt;Java 25 JDK Download&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://x.com/briangoetz" rel="noopener noreferrer"&gt;Brian Goetz (Java Language Architect)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Spring Framework:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://spring.io/" rel="noopener noreferrer"&gt;Spring.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spring.io/guides" rel="noopener noreferrer"&gt;Spring Guides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-boot/4.0/" rel="noopener noreferrer"&gt;Spring Boot 4 Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-framework/reference/" rel="noopener noreferrer"&gt;Spring Framework 7 Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-ai/reference/" rel="noopener noreferrer"&gt;Spring AI Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;Spring Initializr&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practice:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://codingbat.com/java" rel="noopener noreferrer"&gt;CodingBat Java&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://exercism.org/tracks/java" rel="noopener noreferrer"&gt;Exercism Java Track&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Book:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.in/d/itC6EeE" rel="noopener noreferrer"&gt;"Java: A Beginner's Guide" by Herbert Schildt (9th Edition)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jetbrains.com/idea/" rel="noopener noreferrer"&gt;IntelliJ IDEA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;Git&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.localstack.cloud/" rel="noopener noreferrer"&gt;LocalStack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://junit.org/junit5/" rel="noopener noreferrer"&gt;JUnit 6&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://maven.apache.org/" rel="noopener noreferrer"&gt;Maven&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gradle.org/" rel="noopener noreferrer"&gt;Gradle&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problem Statements:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sih.gov.in/" rel="noopener noreferrer"&gt;Smart India Hackathon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sih.gov.in/sih2025PS" rel="noopener noreferrer"&gt;SIH Problem Statements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Divyansh Bhardwaj is a Corporate and Technical Trainer specializing in Java, Spring, and Full-Stack Development. He has trained thousands of developers across educational institutions in India since 2018.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connect:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
GitHub &lt;a href="https://github.com/dbc2201" rel="noopener noreferrer"&gt;@dbc2201&lt;/a&gt;&lt;br&gt;
LinkedIn (&lt;a href="https://www.linkedin.com/in/divyansh-bhardwaj-0850b07b/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/divyansh-bhardwaj-0850b07b/&lt;/a&gt;)&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; #java #programming #beginners #roadmap #career #springboot #webdev&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>java</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>From Philosophy to Practice: Building Your First GraphQL API with Spring Boot 4</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Thu, 08 Jan 2026 21:49:14 +0000</pubDate>
      <link>https://dev.to/dbc2201/from-philosophy-to-practice-building-your-first-graphql-api-with-spring-boot-4-2294</link>
      <guid>https://dev.to/dbc2201/from-philosophy-to-practice-building-your-first-graphql-api-with-spring-boot-4-2294</guid>
      <description>&lt;h2&gt;
  
  
  This post is for you if:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You read (or skimmed) my &lt;a href="https://dev.to/link-to-previous-post"&gt;REST vs GraphQL: Two Philosophies, Two Eras, One Endless Debate&lt;/a&gt; article&lt;/li&gt;
&lt;li&gt;You nodded along to "GraphQL thinks in queries, the client knows best"&lt;/li&gt;
&lt;li&gt;Then you thought: "Cool philosophy, but how do I actually &lt;em&gt;build&lt;/em&gt; one?"&lt;/li&gt;
&lt;li&gt;You're a Java/Spring developer who learns by doing, not just reading&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If that's you, welcome to Part 2. Philosophy time is over. Now we build.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Context: Where We Left Off
&lt;/h2&gt;

&lt;p&gt;In the previous article, I argued that GraphQL was born from Facebook's 2011 mobile nightmare—the realization that fetching a News Feed story shouldn't require 5+ HTTP round trips. We explored the philosophical difference:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;REST asks:&lt;/strong&gt; "What resource do you want?"&lt;br&gt;&lt;br&gt;
&lt;strong&gt;GraphQL asks:&lt;/strong&gt; "What data do you need?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We also saw Spring Framework 7 embrace API versioning as a first-class citizen, acknowledging that the real world is messier than academic purity.&lt;/p&gt;

&lt;p&gt;Now, let's see what "the client knows best" looks like in actual Java code.&lt;/p&gt;




&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;A simple Book API. Nothing fancy—just enough to understand the core concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query a book by ID&lt;/li&gt;
&lt;li&gt;Get the book's author (demonstrating nested data fetching)&lt;/li&gt;
&lt;li&gt;See how GraphQL resolves relationships without multiple endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end, you'll have a running GraphQL server and understand &lt;em&gt;why&lt;/em&gt; the code is structured the way it is.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java 25&lt;/strong&gt; (LTS as of September 2025—or Java 21 if you're not ready to upgrade yet)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maven 3.9.12&lt;/strong&gt; (the current stable release)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your favorite IDE&lt;/strong&gt; (IntelliJ IDEA, VS Code with Java extensions, or even vim if you're that person)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Create the Project
&lt;/h2&gt;

&lt;p&gt;Head to &lt;a href="https://start.spring.io/" rel="noopener noreferrer"&gt;Spring Initializr&lt;/a&gt; and configure:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Project&lt;/td&gt;
&lt;td&gt;Maven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spring Boot&lt;/td&gt;
&lt;td&gt;4.0.1 (latest stable)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Group&lt;/td&gt;
&lt;td&gt;&lt;code&gt;com.example&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Artifact&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bookstore-graphql&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bookstore-graphql&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packaging&lt;/td&gt;
&lt;td&gt;Jar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Dependencies to add:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring Web&lt;/li&gt;
&lt;li&gt;Spring for GraphQL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Generate&lt;/strong&gt;, unzip, and open in your IDE.&lt;/p&gt;

&lt;p&gt;Alternatively, here's the &lt;code&gt;pom.xml&lt;/code&gt; you should end up with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;project&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://maven.apache.org/POM/4.0.0"&lt;/span&gt;
         &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt;
         &lt;span class="na"&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"http://maven.apache.org/POM/4.0.0 
         https://maven.apache.org/xsd/maven-4.0.0.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;modelVersion&amp;gt;&lt;/span&gt;4.0.0&lt;span class="nt"&gt;&amp;lt;/modelVersion&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;parent&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-parent&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;4.0.1&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;relativePath/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/parent&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.example&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;bookstore-graphql&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.0.1-SNAPSHOT&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;name&amp;gt;&lt;/span&gt;bookstore-graphql&lt;span class="nt"&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;description&amp;gt;&lt;/span&gt;GraphQL API with Spring Boot 4&lt;span class="nt"&gt;&amp;lt;/description&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;java.version&amp;gt;&lt;/span&gt;25&lt;span class="nt"&gt;&amp;lt;/java.version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!-- Spring Web (still needed for HTTP transport) --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="c"&gt;&amp;lt;!-- Spring for GraphQL --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-graphql&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

        &lt;span class="c"&gt;&amp;lt;!-- Testing --&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.graphql&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-graphql-test&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;test&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/project&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A note on versions:&lt;/strong&gt; Spring Boot 4.0.1 ships with Spring for GraphQL 2.0.1, Spring Framework 7.0.2, and GraphQL Java 25.0. The versions are managed by the parent POM, so you don't need to specify them explicitly. This is one of Spring Boot's gifts to humanity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Define the Schema (Schema-First Development)
&lt;/h2&gt;

&lt;p&gt;Here's something important: &lt;strong&gt;GraphQL is schema-first by design.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike REST, where you might design your endpoints as you go, GraphQL forces you to think about your data contract upfront. The schema &lt;em&gt;is&lt;/em&gt; the API documentation. The schema &lt;em&gt;is&lt;/em&gt; the contract between client and server.&lt;/p&gt;

&lt;p&gt;Create the file &lt;code&gt;src/main/resources/graphql/schema.graphqls&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;bookById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!):&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;allBooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;!]!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Book&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;pageCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Author&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me break down what's happening here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;type Query&lt;/code&gt;&lt;/strong&gt; — This is the entry point. Every GraphQL API has a Query type that defines what clients can ask for. Think of it as your "read operations" menu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;bookById(id: ID!): Book&lt;/code&gt;&lt;/strong&gt; — A query that takes a required (&lt;code&gt;!&lt;/code&gt;) ID and returns a Book. The &lt;code&gt;!&lt;/code&gt; means "non-null"—the client &lt;em&gt;must&lt;/em&gt; provide this argument.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;[Book!]!&lt;/code&gt;&lt;/strong&gt; — This reads as: "a non-null array of non-null Books." Yes, you can have null arrays OR arrays with null elements in GraphQL. The &lt;code&gt;!&lt;/code&gt; placement matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nested types&lt;/strong&gt; — Notice how &lt;code&gt;Book&lt;/code&gt; has an &lt;code&gt;author&lt;/code&gt; field of type &lt;code&gt;Author&lt;/code&gt;. This is where GraphQL shines. The client can ask for just the book title, or dive deep into author details—same endpoint, same schema, different queries.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Create the Domain Models
&lt;/h2&gt;

&lt;p&gt;Now we need Java classes that match our schema. Java records are perfect for this—immutable, concise, and they generate &lt;code&gt;equals()&lt;/code&gt;, &lt;code&gt;hashCode()&lt;/code&gt;, and &lt;code&gt;toString()&lt;/code&gt; for free.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;src/main/java/com/example/bookstoregraphql/model/Book.java&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.bookstoregraphql.model&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;pageCount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;authorId&lt;/span&gt;  &lt;span class="c1"&gt;// Note: we store the ID, not the full Author object&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Sample data - in reality, this would come from a database&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;BOOKS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"book-1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Harry Potter and the Philosopher's Stone"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;223&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"author-1"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"book-2"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Moby Dick"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;635&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"author-2"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"book-3"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Interview with the Vampire"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;371&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"author-3"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"book-4"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"The Great Gatsby"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"author-4"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"book-5"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Clean Code"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;464&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"author-5"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="nf"&gt;getById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;BOOKS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findFirst&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;BOOKS&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create &lt;code&gt;src/main/java/com/example/bookstoregraphql/model/Author.java&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.bookstoregraphql.model&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;AUTHORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;util&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;List&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"author-1"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Joanne"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Rowling"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"author-2"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Herman"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Melville"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"author-3"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Anne"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Rice"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"author-4"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"F. Scott"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Fitzgerald"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"author-5"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Robert C."&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Martin"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt; &lt;span class="nf"&gt;getById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;AUTHORS&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findFirst&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Production Note:&lt;/strong&gt; We're using static lists inside records for simplicity. In a real application, you'd inject &lt;code&gt;BookRepository&lt;/code&gt; and &lt;code&gt;AuthorRepository&lt;/code&gt; (Spring Data JPA, for example) and fetch data from a database. The GraphQL resolution pattern stays the same—only the data source changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;authorId&lt;/code&gt; instead of &lt;code&gt;Author&lt;/code&gt; in the Book record?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a deliberate design choice. In the GraphQL schema, &lt;code&gt;Book.author&lt;/code&gt; is of type &lt;code&gt;Author&lt;/code&gt;. But in our Java model, we store &lt;code&gt;authorId&lt;/code&gt; as a String.&lt;/p&gt;

&lt;p&gt;Why? Because &lt;strong&gt;GraphQL resolves fields lazily&lt;/strong&gt;. If the client only asks for &lt;code&gt;book.title&lt;/code&gt;, we never need to fetch the author. If they ask for &lt;code&gt;book.author.firstName&lt;/code&gt;, &lt;em&gt;then&lt;/em&gt; we resolve it. Storing the full Author object would force eager loading—exactly the N+1 problem we're trying to avoid.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Create the Controller (Data Fetchers)
&lt;/h2&gt;

&lt;p&gt;This is where the magic happens. Spring for GraphQL uses annotations that feel familiar if you've written REST controllers, but they work fundamentally differently.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;src/main/java/com/example/bookstoregraphql/controller/BookController.java&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.bookstoregraphql.controller&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.example.bookstoregraphql.model.Author&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.example.bookstoregraphql.model.Book&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.graphql.data.method.annotation.Argument&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.graphql.data.method.annotation.QueryMapping&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.graphql.data.method.annotation.SchemaMapping&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.stereotype.Controller&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.List&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Controller&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Maps to: Query.bookById(id: ID!)&lt;/span&gt;
    &lt;span class="nd"&gt;@QueryMapping&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="nf"&gt;bookById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Argument&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Maps to: Query.allBooks&lt;/span&gt;
    &lt;span class="nd"&gt;@QueryMapping&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;allBooks&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Maps to: Book.author&lt;/span&gt;
    &lt;span class="c1"&gt;// This is called ONLY when the client requests the author field&lt;/span&gt;
    &lt;span class="nd"&gt;@SchemaMapping&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt; &lt;span class="nf"&gt;author&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Book&lt;/span&gt; &lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;book&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let me explain these annotations because they're doing a lot of work:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@QueryMapping&lt;/code&gt;&lt;/strong&gt; — Binds this method to a field in the &lt;code&gt;Query&lt;/code&gt; type. The method name (&lt;code&gt;bookById&lt;/code&gt;) must match the field name in your schema. Spring for GraphQL registers this as a "data fetcher" for that field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@Argument&lt;/code&gt;&lt;/strong&gt; — Extracts a named argument from the GraphQL query. The parameter name (&lt;code&gt;id&lt;/code&gt;) must match the argument name in your schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@SchemaMapping&lt;/code&gt;&lt;/strong&gt; — This is the interesting one. It binds to a field on a &lt;em&gt;type&lt;/em&gt; (not Query). When a client asks for &lt;code&gt;book.author&lt;/code&gt;, GraphQL needs to know how to resolve that. The first parameter (&lt;code&gt;Book book&lt;/code&gt;) tells Spring: "When resolving the &lt;code&gt;author&lt;/code&gt; field on a &lt;code&gt;Book&lt;/code&gt;, give me the parent Book object."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; &lt;code&gt;@SchemaMapping&lt;/code&gt; methods are only called when needed. If the client's query doesn't include &lt;code&gt;author&lt;/code&gt;, this method never executes. That's lazy resolution in action.&lt;/p&gt;




&lt;h2&gt;
  
  
  Checkpoint: What We've Built So Far
&lt;/h2&gt;

&lt;p&gt;Before we test anything, let's make sure the mental model is clear.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema → Code Mapping:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;GraphQL Schema&lt;/th&gt;
&lt;th&gt;Java Code&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type Query&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@QueryMapping&lt;/code&gt; methods&lt;/td&gt;
&lt;td&gt;Entry points for reads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type Book&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;record Book(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Domain model&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;author: Author!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@SchemaMapping Author author(Book book)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lazy-resolved field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id: ID!&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;String id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GraphQL &lt;code&gt;ID&lt;/code&gt; maps to &lt;code&gt;String&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pageCount: Int&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Integer pageCount&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nullable in schema → &lt;code&gt;Integer&lt;/code&gt; (not &lt;code&gt;int&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The Resolution Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client sends a query to &lt;code&gt;/graphql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Spring matches &lt;code&gt;bookById&lt;/code&gt; to &lt;code&gt;@QueryMapping public Book bookById(...)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If the query includes &lt;code&gt;author&lt;/code&gt;, Spring calls &lt;code&gt;@SchemaMapping public Author author(Book book)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If the query omits &lt;code&gt;author&lt;/code&gt;, that method is never invoked&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the "client knows best" philosophy in action. The server doesn't decide what to return—it &lt;em&gt;reacts&lt;/em&gt; to what's requested.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧪 Quick Check:&lt;/strong&gt; Before moving on, can you predict what happens if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query &lt;code&gt;bookById&lt;/code&gt; with an ID that doesn't exist?&lt;/li&gt;
&lt;li&gt;Query &lt;code&gt;allBooks&lt;/code&gt; but only request &lt;code&gt;title&lt;/code&gt;—does the &lt;code&gt;author()&lt;/code&gt; method run?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hold those predictions. We'll test them in a moment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Enable GraphiQL (The Interactive Playground)
&lt;/h2&gt;

&lt;p&gt;GraphiQL is an in-browser IDE for exploring GraphQL APIs. It provides auto-completion, documentation browsing, and query history. Essential for development.&lt;/p&gt;

&lt;p&gt;Add to &lt;code&gt;src/main/resources/application.properties&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# Enable GraphiQL playground
&lt;/span&gt;&lt;span class="py"&gt;spring.graphql.graphiql.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;

&lt;span class="c"&gt;# Optional: customize the path (default is /graphiql)
# spring.graphql.graphiql.path=/playground
&lt;/span&gt;
&lt;span class="c"&gt;# Optional: show schema in GraphiQL
&lt;/span&gt;&lt;span class="py"&gt;spring.graphql.schema.printer.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Run and Test
&lt;/h2&gt;

&lt;p&gt;Start the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mvnw spring-boot:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or from your IDE, run the main class.&lt;/p&gt;

&lt;p&gt;Open your browser to: &lt;strong&gt;&lt;a href="http://localhost:8080/graphiql" rel="noopener noreferrer"&gt;http://localhost:8080/graphiql&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You should see the GraphiQL interface. Now let's run some queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query 1: Get a book with minimal fields
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;bookById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"book-1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bookById"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"book-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harry Potter and the Philosopher's Stone"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice: We didn't ask for &lt;code&gt;author&lt;/code&gt;, so the &lt;code&gt;author()&lt;/code&gt; method in our controller was never called. No unnecessary database hits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query 2: Get a book with author details
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;bookById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"book-1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;pageCount&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bookById"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"book-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harry Potter and the Philosopher's Stone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pageCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;223&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Joanne"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rowling"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same endpoint. Different data. Client decides.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧪 Try this yourself:&lt;/strong&gt; Before moving on, modify the query above to only request &lt;code&gt;pageCount&lt;/code&gt;. Notice what happens—you get exactly what you asked for, nothing more. Now add &lt;code&gt;author { firstName }&lt;/code&gt; back in. Watch how the response shape changes based purely on your query.&lt;/p&gt;

&lt;h3&gt;
  
  
  Query 3: Get all books (with selective fields)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;allBooks&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allBooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harry Potter and the Philosopher's Stone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rowling"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Moby Dick"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Melville"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Interview with the Vampire"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rice"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Great Gatsby"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fitzgerald"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Clean Code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Martin"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Query 4: Multiple queries in one request
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;potter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bookById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"book-1"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;cleanCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;bookById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"book-5"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Response:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"potter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Harry Potter and the Philosopher's Stone"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cleanCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Clean Code"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"firstName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Robert C."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lastName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Martin"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two queries, one HTTP request. Aliases (&lt;code&gt;potter:&lt;/code&gt;, &lt;code&gt;cleanCode:&lt;/code&gt;) let you query the same field multiple times with different arguments.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Just Happened?
&lt;/h2&gt;

&lt;p&gt;Let's reflect on what we built and &lt;em&gt;why&lt;/em&gt; it matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  The REST Equivalent Would Have Been:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/books/book-1                    → Full book object (whether you need it all or not)
GET /api/books/book-1?include=author     → Custom query param handling
GET /api/books/book-1/author             → Separate endpoint for author
GET /api/books                           → All books (hope you don't need authors too)
GET /api/books?include=author            → More custom handling
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five endpoints (at minimum), plus decisions about query parameters, response shapes, and documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  With GraphQL We Have:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /graphql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One endpoint. The query itself describes what you want. The schema documents what's possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tradeoffs (Because There Are Always Tradeoffs):
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Client flexibility&lt;/td&gt;
&lt;td&gt;Learning curve for clients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Self-documenting schema&lt;/td&gt;
&lt;td&gt;Schema maintenance burden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No over-fetching&lt;/td&gt;
&lt;td&gt;Potential for expensive queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Single endpoint&lt;/td&gt;
&lt;td&gt;Harder to cache at HTTP level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strong typing&lt;/td&gt;
&lt;td&gt;More upfront design work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The N+1 Problem: A Preview
&lt;/h2&gt;

&lt;p&gt;Remember the N+1 problem from the previous article? Our current implementation has it.&lt;/p&gt;

&lt;p&gt;If you call &lt;code&gt;allBooks&lt;/code&gt; and request &lt;code&gt;author&lt;/code&gt; for each, the &lt;code&gt;author()&lt;/code&gt; method gets called once per book. Five books = five &lt;code&gt;Author.getById()&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;In a real application with a database, that's five SQL queries when one would suffice.&lt;/p&gt;

&lt;p&gt;The solution? &lt;strong&gt;Batching&lt;/strong&gt;. Spring for GraphQL provides two options: the traditional &lt;code&gt;DataLoader&lt;/code&gt; pattern (from GraphQL Java) or the Spring-native &lt;code&gt;@BatchMapping&lt;/code&gt; annotation. Both batch those five requests into a single fetch. We'll cover both approaches in the next article.&lt;/p&gt;

&lt;p&gt;For now, just know: &lt;strong&gt;production GraphQL requires batching&lt;/strong&gt;. Don't skip it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Structure Recap
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bookstore-graphql/
├── pom.xml
└── src/main/
    ├── java/com/example/bookstoregraphql/
    │   ├── BookstoreGraphqlApplication.java
    │   ├── controller/
    │   │   └── BookController.java
    │   └── model/
    │       ├── Book.java
    │       └── Author.java
    └── resources/
        ├── application.properties
        └── graphql/
            └── schema.graphqls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Troubleshooting Common Issues
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SchemaResourceResolver: No schema files found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Schema file in wrong location&lt;/td&gt;
&lt;td&gt;Must be at &lt;code&gt;src/main/resources/graphql/schema.graphqls&lt;/code&gt; (note the &lt;code&gt;.graphqls&lt;/code&gt; extension, not &lt;code&gt;.graphql&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;No DataFetcher for field 'bookById'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Method name doesn't match schema&lt;/td&gt;
&lt;td&gt;Ensure &lt;code&gt;@QueryMapping&lt;/code&gt; method name matches the field name in your schema exactly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Cannot resolve symbol 'QueryMapping'&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Missing import&lt;/td&gt;
&lt;td&gt;Add: &lt;code&gt;import org.springframework.graphql.data.method.annotation.*;&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GraphiQL shows blank page&lt;/td&gt;
&lt;td&gt;GraphiQL not enabled&lt;/td&gt;
&lt;td&gt;Ensure &lt;code&gt;spring.graphql.graphiql.enabled=true&lt;/code&gt; in &lt;code&gt;application.properties&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;NullPointerException&lt;/code&gt; in &lt;code&gt;author()&lt;/code&gt; method&lt;/td&gt;
&lt;td&gt;Book has authorId with no matching Author&lt;/td&gt;
&lt;td&gt;Add null check: &lt;code&gt;return author != null ? author : null;&lt;/code&gt; or ensure all author IDs have matching entries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port 8080 already in use&lt;/td&gt;
&lt;td&gt;Another app running&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;server.port=8081&lt;/code&gt; to &lt;code&gt;application.properties&lt;/code&gt; or stop the other process&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;This tutorial covered the basics: schema definition, data fetchers, and the mental model of GraphQL resolution. But production GraphQL involves more:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DataLoader and batching&lt;/strong&gt; — Solving the N+1 problem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutations&lt;/strong&gt; — Creating, updating, and deleting data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subscriptions&lt;/strong&gt; — Real-time data with WebSocket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error handling&lt;/strong&gt; — Partial responses and error types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication &amp;amp; Authorization&lt;/strong&gt; — Securing your schema&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testing&lt;/strong&gt; — Spring GraphQL's testing utilities&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If there's interest, I'll continue this series. Let me know in the comments which topic you'd want next.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;GraphQL isn't magic. It's a different way of thinking about data access—one where the client describes what it needs rather than hoping the server guessed correctly.&lt;/p&gt;

&lt;p&gt;Spring for GraphQL makes this approachable for Java developers. The annotations feel familiar. The integration with Spring Boot is seamless. And with Java records, your domain models stay clean.&lt;/p&gt;

&lt;p&gt;But remember what I said in the philosophy article:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Technologies carry the DNA of the problems they solved.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GraphQL carries Facebook's DNA: complex relationships, mobile-first thinking, and client-driven data needs. If your use case matches, GraphQL is powerful. If you're building simple CRUD APIs for internal services, REST might still be the pragmatic choice.&lt;/p&gt;

&lt;p&gt;The skill isn't knowing GraphQL. The skill is knowing &lt;em&gt;when&lt;/em&gt; to use it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.spring.io/spring-graphql/reference/" rel="noopener noreferrer"&gt;Spring for GraphQL Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://leanpub.com/graphql-java" rel="noopener noreferrer"&gt;GraphQL Java Book&lt;/a&gt; by Andreas Marek (the GraphQL Java maintainer)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spec.graphql.org/" rel="noopener noreferrer"&gt;GraphQL Specification&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/your-repo/bookstore-graphql" rel="noopener noreferrer"&gt;Source code for this tutorial&lt;/a&gt; &lt;em&gt;(coming soon)&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Have questions? Drop a comment below. And if you haven't read the philosophy piece yet, &lt;a href="https://dev.to/link-to-previous-post"&gt;start there&lt;/a&gt;—it'll make everything here click.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>java</category>
      <category>springboot</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>REST vs GraphQL: Two Philosophies, Two Eras, One Endless Debate</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Thu, 08 Jan 2026 20:56:52 +0000</pubDate>
      <link>https://dev.to/dbc2201/rest-vs-graphql-two-philosophies-two-eras-one-endless-debate-1i1a</link>
      <guid>https://dev.to/dbc2201/rest-vs-graphql-two-philosophies-two-eras-one-endless-debate-1i1a</guid>
      <description>&lt;h2&gt;
  
  
  This post is for you if you can answer "yes" to all/most of the questions below:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You've built APIs before (or at least consumed them).&lt;/li&gt;
&lt;li&gt;You've heard the "REST vs GraphQL" debate and felt confused about which one is "better."&lt;/li&gt;
&lt;li&gt;You're tired of surface-level comparisons that just list features.&lt;/li&gt;
&lt;li&gt;You want to &lt;em&gt;understand&lt;/em&gt; why these technologies exist, not just &lt;em&gt;how&lt;/em&gt; to use them.&lt;/li&gt;
&lt;li&gt;You're the kind of developer who asks "why?" before "how?"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you nodded along, stick around. This isn't another comparison table. This is the story of two philosophies born in different eras, solving different problems, and somehow ending up at the same dinner table in 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Misconception That Needs to Die
&lt;/h2&gt;

&lt;p&gt;Let me start with something that might hurt: &lt;strong&gt;REST was never designed for building APIs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Wait, what?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I know. I had the same reaction when I first read Roy Fielding's 2000 dissertation properly. As a trainer who's been explaining REST to developers for years, this was humbling.&lt;/p&gt;

&lt;p&gt;Consider this: Roy Fielding wasn't some random PhD student who sat in an ivory tower and came up with a bright idea. He was deeply embedded in the web's early development. He co-authored HTTP 1.0 and 1.1. He co-founded the Apache HTTP Server project. The man literally helped build the plumbing of the internet.&lt;/p&gt;

&lt;p&gt;REST (Representational State Transfer) was his doctoral dissertation's attempt to &lt;em&gt;describe&lt;/em&gt; and &lt;em&gt;formalize&lt;/em&gt; the architecture of the World Wide Web itself. The entire web is supposed to be RESTful. REST isn't specifically about JSON APIs or &lt;code&gt;GET /users/123&lt;/code&gt;. In fact, Fielding doesn't mention web APIs much in his dissertation at all.&lt;/p&gt;

&lt;p&gt;This distinction matters more than most developers realize.&lt;/p&gt;




&lt;h2&gt;
  
  
  So How Did REST Become the "API Standard"?
&lt;/h2&gt;

&lt;p&gt;Here's where history gets interesting.&lt;/p&gt;

&lt;p&gt;In the mid-2000s, developers were burning in SOAP hell. SOAP (Simple Object Access Protocol) was verbose, complicated, and required understanding a maze of XML specifications just to say "Hello World." There's nothing simple about it, by the way.&lt;/p&gt;

&lt;p&gt;Developers needed an escape route. They needed their own four-letter acronym to rally behind.&lt;/p&gt;

&lt;p&gt;And REST was right there. It was simple. It used HTTP methods everyone already knew. It didn't require a 400-page PDF manual (looking at you, Salesforce's first API). When Ruby on Rails dropped SOAP support in 2007, David Heinemeier Hansson essentially said SOAP was overly complicated, and the migration was on.&lt;/p&gt;

&lt;p&gt;The problem? Most of what we call "REST APIs" today would make Roy Fielding cringe. As he clarified in a 2008 blog post:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"A REST API should be entered with no prior knowledge beyond the initial URI and set of standardized media types."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most APIs hardcode everything. Your &lt;code&gt;/api/v1/users&lt;/code&gt; endpoint with JSON responses? That's not really RESTful in the pure sense. It's just "HTTP-based API that kinda follows some REST principles."&lt;/p&gt;

&lt;p&gt;But does that matter? Honestly, not really. What matters is that REST gave us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt;: HTTP verbs (GET, POST, PUT, DELETE) mapped to CRUD operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Statelessness&lt;/strong&gt;: Each request contains everything needed to process it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cacheability&lt;/strong&gt;: HTTP caching worked out of the box&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A shared vocabulary&lt;/strong&gt;: Developers worldwide could talk to each other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;REST became the &lt;em&gt;lingua franca&lt;/em&gt; of APIs. And that's powerful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter Facebook's Mobile Nightmare (2011)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;2011, Menlo Park.&lt;/strong&gt; Facebook had a problem.&lt;/p&gt;

&lt;p&gt;Their iOS and Android apps were thin wrappers around mobile web views—and they were failing. Slow load times. Frequent crashes. Users abandoning the app entirely. Mark Zuckerberg would later call this approach "one of the biggest mistakes we've made as a company."&lt;/p&gt;

&lt;p&gt;Three engineers—Lee Byron, Dan Schafer, and Nick Schrock—took on the challenge of rebuilding the News Feed as a native iOS app. They hit a wall immediately: the backend was architected for web pages, not mobile clients.&lt;/p&gt;

&lt;p&gt;Consider what a single News Feed story contains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Author name and profile picture&lt;/li&gt;
&lt;li&gt;Post content (text, images, or video)&lt;/li&gt;
&lt;li&gt;Like count&lt;/li&gt;
&lt;li&gt;Comments (each with its own author, timestamp, and content)&lt;/li&gt;
&lt;li&gt;Shares (which are themselves posts, recursively containing all of the above)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a traditional REST approach, loading one story could require:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /posts/123
GET /users/456          ← author
GET /posts/123/comments
GET /posts/123/likes
GET /users/789          ← commenter 1
GET /users/101          ← commenter 2
...and so on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a 2012-era 3G connection, this was death by a thousand round trips.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REST: Multiple Round Trips&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh641cl723k57digtgls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh641cl723k57digtgls.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GraphQL: Single Round Trip&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh2mh9uq1a9nvav71wrf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh2mh9uq1a9nvav71wrf.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(To be fair: REST has patterns like embedded resources (&lt;code&gt;?include=author,comments&lt;/code&gt;) and sparse fieldsets that can reduce this. But they require deliberate design work—they're not built into the paradigm the way GraphQL's single-query model is.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;They tried the existing internal REST-like API. They tried FQL (Facebook Query Language), their SQL-like query language. But both had the same fundamental problem: &lt;strong&gt;the data was relational and graph-like, but the APIs were tabular and rigid.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As Lee Byron put it: "You've got a square-peg, round-hole problem on the server and a round-peg, square-hole problem on the client."&lt;/p&gt;

&lt;p&gt;The solution? Stop treating data like tables. Start treating it like what it actually is: &lt;strong&gt;a graph&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They called the prototype "SuperGraph." The world would eventually know it as &lt;strong&gt;GraphQL&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Philosophy Difference
&lt;/h2&gt;

&lt;p&gt;This is where it gets philosophical. Bear with me: this is the part that'll actually make you &lt;em&gt;think&lt;/em&gt; differently about APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  REST's Philosophy: The Web of Resources
&lt;/h3&gt;

&lt;p&gt;REST thinks in &lt;strong&gt;resources&lt;/strong&gt;. Every thing is a thing with an address.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user is a resource: &lt;code&gt;/users/123&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A post is a resource: &lt;code&gt;/posts/456&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Comments are resources: &lt;code&gt;/posts/456/comments&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You interact with these resources using standard HTTP verbs. The server decides what data you get back. The client adapts.&lt;/p&gt;

&lt;p&gt;Think of it like a library catalog system: every book has a unique call number (its URL), you follow standard procedures to borrow or return it (HTTP verbs), and the librarian decides what information goes on the checkout slip (the response). You can't ask for "just the first chapter"—you get the whole book.&lt;/p&gt;

&lt;p&gt;This made perfect sense for the web. URLs, HTTP methods, caching—it all mapped beautifully to how browsers worked.&lt;/p&gt;

&lt;h3&gt;
  
  
  GraphQL's Philosophy: The Client Knows Best
&lt;/h3&gt;

&lt;p&gt;GraphQL thinks in &lt;strong&gt;queries&lt;/strong&gt;. The client describes exactly what it wants, and the server fulfills that request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;avatar&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;likes&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;comments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One request. Exactly the data needed. Nothing more, nothing less.&lt;/p&gt;

&lt;p&gt;This made perfect sense for mobile apps. Battery life matters. Bandwidth costs money. Every unnecessary byte is a crime.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Tension
&lt;/h3&gt;

&lt;p&gt;The fundamental difference comes down to this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;REST&lt;/th&gt;
&lt;th&gt;GraphQL&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Who decides the data shape?&lt;/td&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How many endpoints?&lt;/td&gt;
&lt;td&gt;Many (per resource)&lt;/td&gt;
&lt;td&gt;One&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data fetching&lt;/td&gt;
&lt;td&gt;Fixed responses&lt;/td&gt;
&lt;td&gt;Flexible queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Versioning&lt;/td&gt;
&lt;td&gt;URL-based (&lt;code&gt;/v1/&lt;/code&gt;, &lt;code&gt;/v2/&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Schema evolution (deprecation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;Built into HTTP&lt;/td&gt;
&lt;td&gt;Requires additional tooling&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Neither is "better." They're solving different problems with different constraints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The takeaway:&lt;/strong&gt; REST asks "what resource do you want?" while GraphQL asks "what data do you need?" That one-word difference—&lt;em&gt;resource&lt;/em&gt; vs &lt;em&gt;data&lt;/em&gt;—explains almost every architectural decision that follows.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Evolution: 2012 to 2026
&lt;/h2&gt;

&lt;p&gt;Let's trace how both have evolved, because this is where the story gets rich.&lt;/p&gt;

&lt;h3&gt;
  
  
  REST's Evolution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;2000-2010: The Wild West&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST becomes the anti-SOAP&lt;/li&gt;
&lt;li&gt;Everyone interprets it differently&lt;/li&gt;
&lt;li&gt;OpenAPI (then Swagger) emerges to bring structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2010-2020: Maturation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JSON becomes the default (goodbye XML!)&lt;/li&gt;
&lt;li&gt;API design best practices solidify&lt;/li&gt;
&lt;li&gt;HATEOAS (Hypermedia as the Engine of Application State—the idea that API responses should include links to related actions) remains mostly theoretical&lt;/li&gt;
&lt;li&gt;OpenAPI 3.0 standardizes documentation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2020-2026: The Pragmatic Era&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST stops pretending to be "pure"&lt;/li&gt;
&lt;li&gt;Hybrid patterns emerge (REST serving GraphQL gateways)&lt;/li&gt;
&lt;li&gt;AI-driven systems start consuming REST APIs en masse&lt;/li&gt;
&lt;li&gt;REST becomes the stable, boring, reliable choice—and that's a compliment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spring Framework 7 adds first-class API versioning&lt;/strong&gt; (September 2025)—more on this below&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GraphQL's Evolution
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;2012-2015: The Secret Years&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built internally at Facebook&lt;/li&gt;
&lt;li&gt;Powers iOS app rewrite (August 2012)&lt;/li&gt;
&lt;li&gt;Users consume 2x more feed stories than the HTML5 app&lt;/li&gt;
&lt;li&gt;Remains internal for 3 years&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2015: The Open-Source Moment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facebook publishes the GraphQL specification&lt;/li&gt;
&lt;li&gt;Reference implementation released&lt;/li&gt;
&lt;li&gt;GitHub builds their public GraphQL API (2016)&lt;/li&gt;
&lt;li&gt;Adoption begins slowly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2017-2020: The Hype Cycle Peak&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Apollo GraphQL emerges as the dominant tooling company&lt;/li&gt;
&lt;li&gt;Every startup wants GraphQL&lt;/li&gt;
&lt;li&gt;"GraphQL replaces REST" becomes the hot take&lt;/li&gt;
&lt;li&gt;Reality: most teams struggle with complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2020-2024: The Sobering&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;N+1 query problems become well-known (when a single GraphQL query triggers dozens of separate database calls—one for the parent, plus one for each child)&lt;/li&gt;
&lt;li&gt;Security concerns (query depth, complexity attacks) require attention&lt;/li&gt;
&lt;li&gt;Teams realize GraphQL needs infrastructure investment&lt;/li&gt;
&lt;li&gt;Federation emerges for microservices architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The N+1 Problem Visualized:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tm09dhgqhtk159bbx8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tm09dhgqhtk159bbx8m.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;1 query for users + N queries for each user's posts = N+1 database hits. Solution: DataLoader batches the N queries into 1.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2025-2026: The Maturation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 years of GraphQL celebrated at GraphQLConf 2025&lt;/li&gt;
&lt;li&gt;Apollo Connectors released—REST APIs can now be declaratively integrated into GraphQL schemas&lt;/li&gt;
&lt;li&gt;GraphQL Foundation standardizes Federation&lt;/li&gt;
&lt;li&gt;MCP (Model Context Protocol) shows early promise for bringing GraphQL to AI agents—still emerging, but worth watching&lt;/li&gt;
&lt;li&gt;The "GraphQL vs REST" debate finally calms down&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The takeaway:&lt;/strong&gt; Both technologies matured by acknowledging their weaknesses. REST stopped pretending versioning wasn't needed; GraphQL stopped pretending it was simple. The survivors are the pragmatists.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Are We Now? (January 2026)
&lt;/h2&gt;

&lt;p&gt;Let me share some numbers that tell the real story:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;REST still dominates&lt;/strong&gt;: 93% of teams use REST, while 33% use GraphQL (&lt;a href="https://www.postman.com/state-of-api/" rel="noopener noreferrer"&gt;Postman State of the API 2025&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL production usage&lt;/strong&gt; has reached 61% among surveyed organizations (&lt;a href="https://hygraph.com/graphql-report-2024" rel="noopener noreferrer"&gt;Hygraph GraphQL Report 2024&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise adoption is accelerating&lt;/strong&gt;: Gartner predicts 60%+ of enterprises will use GraphQL in production by 2027, up from less than 30% in 2024 (&lt;a href="https://thenewstack.io/graphql-growth-explodes-but-so-do-problems-federated-graphs-solve/" rel="noopener noreferrer"&gt;The New Stack&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid approaches&lt;/strong&gt; are becoming the norm—companies running both report higher satisfaction than those committed exclusively to either&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;(Note: We're focusing on REST and GraphQL here, but gRPC deserves mention as the third major player—especially for high-performance internal microservice communication.)&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Pause for a moment:&lt;/strong&gt; Based on what you've read so far, which philosophy would you lean toward for a mobile-first e-commerce app? Hold that thought—we'll come back to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Spring Framework 7 Plot Twist
&lt;/h2&gt;

&lt;p&gt;Here's something that dropped in September 2025 that perfectly encapsulates the state of this debate.&lt;/p&gt;

&lt;p&gt;Spring Framework 7 (releasing November 2025) added &lt;strong&gt;first-class API versioning support&lt;/strong&gt;. For those of us in the Java/Spring world, this is huge. Rossen Stoyanchev from the Spring team &lt;a href="https://spring.io/blog/2025/09/16/api-versioning-in-spring" rel="noopener noreferrer"&gt;wrote about it&lt;/a&gt;, and the irony is thick.&lt;/p&gt;

&lt;p&gt;While this is Java-specific, it signals a broader industry shift. NestJS has been moving toward declarative versioning, FastAPI added versioning patterns in recent releases, and even Go frameworks like Gin are seeing community pressure for standardized approaches. The enterprise world is collectively admitting: &lt;em&gt;versioning isn't going away, so let's do it right.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The key insight: versioning has always been REST's Achilles heel. Do you version in the URL (&lt;code&gt;/v1/users&lt;/code&gt;)? In a header (&lt;code&gt;API-Version: 1.1&lt;/code&gt;)? In the media type (&lt;code&gt;application/vnd.myapi.v1+json&lt;/code&gt;)? Everyone has opinions. Nobody agrees. There's no standard.&lt;/p&gt;

&lt;p&gt;And guess what Roy Fielding, the creator of REST, says about API versioning? &lt;strong&gt;He advises against it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yet here's Spring, the enterprise Java juggernaut, adding official versioning support because, as the issue tracker showed, implementing it properly "requires a lot of work."&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mechanics
&lt;/h3&gt;

&lt;p&gt;The idea is simple: configure your versioning strategy once, then let the framework handle routing based on version metadata. No more hacking URL patterns or writing custom interceptors. The framework treats version as a first-class routing concern, just like HTTP method or content type.&lt;/p&gt;

&lt;p&gt;The new &lt;code&gt;ApiVersionStrategy&lt;/code&gt; lets you configure versioning once and use it everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WebConfiguration&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;WebMvcConfigurer&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;configureApiVersioning&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApiVersionConfigurer&lt;/span&gt; &lt;span class="n"&gt;configurer&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;configurer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;useRequestHeader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"API-Version"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or in Spring Boot 4, just a property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;spring.mvc.apiversion.use.header&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;API-Version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then your controllers become beautifully explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/account/{id}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt; &lt;span class="nf"&gt;getAccountV1&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Version 1.1 implementation&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/account/{id}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2.0"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;AccountV2&lt;/span&gt; &lt;span class="nf"&gt;getAccountV2&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Version 2.0 implementation&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They even added &lt;strong&gt;baseline versions&lt;/strong&gt; (&lt;code&gt;"1.2+"&lt;/code&gt;) so you can say "this endpoint works for version 1.2 and all future versions until something breaks," reducing the need to duplicate controller methods for unchanged endpoints.&lt;/p&gt;

&lt;p&gt;For deprecation? Built-in support for RFC 9745 and RFC 8594 headers (&lt;code&gt;Deprecation&lt;/code&gt;, &lt;code&gt;Sunset&lt;/code&gt;, &lt;code&gt;Link&lt;/code&gt;). The server can actually tell clients "hey, you're using an old version, here's when it dies."&lt;/p&gt;

&lt;h3&gt;
  
  
  The Philosophical Implication
&lt;/h3&gt;

&lt;p&gt;This is REST admitting something GraphQL advocates have said for years: &lt;strong&gt;managing API versions is hard, and most teams do it poorly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But GraphQL's answer to versioning is fundamentally different: "don't version—evolve." You add new fields, deprecate old ones, but you never break the schema. The query language itself allows clients to request only what they need, so adding fields doesn't break anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# New field - old clients just don't request it&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="c"&gt;# Deprecated field - still works, but marked for removal&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;legacyId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;deprecated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;instead&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But here's the reality check: &lt;strong&gt;GraphQL's "no versioning" philosophy requires discipline.&lt;/strong&gt; You can't remove fields easily. You can't rename them. Every change must be additive. And if you've ever worked in a fast-moving team, you know how hard it is to maintain that discipline forever. (This is why mature GraphQL teams use &lt;strong&gt;Schema Registries&lt;/strong&gt; with breaking change detection in CI/CD—the tooling enforces what humans forget.)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;REST's Approach&lt;/th&gt;
&lt;th&gt;GraphQL's Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Explicit versions (&lt;code&gt;v1&lt;/code&gt;, &lt;code&gt;v2&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Schema evolution (deprecation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple endpoints coexist&lt;/td&gt;
&lt;td&gt;Single endpoint, schema changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Breaking changes are clear&lt;/td&gt;
&lt;td&gt;Additive-only changes encouraged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clients choose their version&lt;/td&gt;
&lt;td&gt;Clients adapt to schema&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Spring's approach says: "Look, versioning is happening. Let's make it first-class so teams do it &lt;em&gt;well&lt;/em&gt; instead of poorly."&lt;/p&gt;

&lt;p&gt;Neither is wrong. They're just different philosophies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL&lt;/strong&gt;: "Let's design a system where versioning isn't needed."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REST (Spring 7)&lt;/strong&gt;: "Let's accept that versioning happens and make it painless."&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Here's what mature teams are doing in 2026:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;REST for public APIs&lt;/strong&gt;: Simple, documented, universally understood&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL for internal composition&lt;/strong&gt;: Complex queries, mobile apps, dashboard applications&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GraphQL Gateways over REST&lt;/strong&gt;: Apollo Connectors and similar tools let you keep REST backends while getting GraphQL benefits&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As Matt DeBergalis (Apollo CTO) said recently: "What would have been a yearlong rewrite has become a two-day implementation."&lt;/p&gt;




&lt;h2&gt;
  
  
  So, Which Should You Learn?
&lt;/h2&gt;

&lt;p&gt;If you're asking me as a trainer who has taught both to hundreds of developers, here's my honest answer:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learn REST first.&lt;/strong&gt; Not because it's better, but because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It builds on HTTP fundamentals you need anyway&lt;/li&gt;
&lt;li&gt;It's what you'll encounter in 80% of real-world projects&lt;/li&gt;
&lt;li&gt;It teaches you to think in resources, which is universally useful&lt;/li&gt;
&lt;li&gt;Debugging REST is simpler&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Then learn GraphQL.&lt;/strong&gt; Because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It will change how you think about data relationships&lt;/li&gt;
&lt;li&gt;Mobile and frontend-heavy projects increasingly demand it&lt;/li&gt;
&lt;li&gt;Schema-first development is a powerful paradigm&lt;/li&gt;
&lt;li&gt;The tooling ecosystem is now mature enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;But most importantly:&lt;/strong&gt; Learn &lt;em&gt;why&lt;/em&gt; they exist. The history matters. The philosophy matters. When you understand that REST was about describing the web's architecture and GraphQL was about solving Facebook's mobile crisis, you stop asking "which is better?" and start asking "which is right for this problem?"&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Lesson
&lt;/h2&gt;

&lt;p&gt;After years of teaching this, one insight stands out: &lt;strong&gt;technologies are not neutral. They carry the DNA of the problems they were born to solve.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;REST carries the DNA of the early web—documents, links, browsers, caching. When your problem looks like that, REST shines.&lt;/p&gt;

&lt;p&gt;GraphQL carries the DNA of Facebook's News Feed—complex relationships, mobile constraints, constantly evolving schemas. When your problem looks like that, GraphQL shines.&lt;/p&gt;

&lt;p&gt;The mistake is treating them as competing answers to the same question. They're not. They're different questions altogether:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST asks: "How do we create a simple, scalable way to access resources over HTTP?"&lt;/li&gt;
&lt;li&gt;GraphQL asks: "How do we let clients efficiently query complex, interconnected data?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both questions are valid. Both have elegant answers. The skill is in knowing which question you're really asking.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your Turn
&lt;/h2&gt;

&lt;p&gt;I'd love to hear from you in the comments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What's been your experience with REST vs GraphQL?&lt;/li&gt;
&lt;li&gt;Have you ever tried running both in the same project?&lt;/li&gt;
&lt;li&gt;What problems did each solve (or create) for you?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And if you're still figuring this out—that's okay. I was too, for years. The best developers I know are the ones who stay curious, keep questioning, and never settle for "because that's how it's done."&lt;/p&gt;

&lt;p&gt;Keep learning. Keep building. And remember: the technology is just a tool. The thinking is what matters.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If this helped you understand the REST vs GraphQL landscape a little better, consider sharing it with someone who's asking the same questions. Let's raise the level of discourse beyond "X is dead, Y is the future."&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;References &amp;amp; Further Reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fielding, Roy. &lt;em&gt;Architectural Styles and the Design of Network-based Software Architectures&lt;/em&gt; (2000). &lt;a href="https://ics.uci.edu/~fielding/pubs/dissertation/top.htm" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Byron, Lee. "Introducing the GraphQL Foundation" (2018). &lt;a href="https://medium.com/@leeb/introducing-the-graphql-foundation-3235d8186d6d" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Meta Engineering. "GraphQL: A data query language" (2015). &lt;a href="https://engineering.fb.com/2015/09/14/core-infra/graphql-a-data-query-language/" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Two-Bit History. "Roy Fielding's Misappropriated REST Dissertation" (2020). &lt;a href="https://twobithistory.org/2020/06/28/rest.html" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GraphQL Foundation. "Federation Documentation" (2025). &lt;a href="https://graphql.org/learn/federation/" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Stoyanchev, Rossen. "API Versioning in Spring" (2025). &lt;a href="https://spring.io/blog/2025/09/16/api-versioning-in-spring" rel="noopener noreferrer"&gt;Link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Stoyanchev, Rossen. "API Versioning Talk at Spring I/O" (2025). &lt;a href="https://www.youtube.com/watch?v=ZA-MzWpKvtQ" rel="noopener noreferrer"&gt;Video&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>api</category>
      <category>architecture</category>
      <category>discuss</category>
      <category>graphql</category>
    </item>
    <item>
      <title>Java's Concurrency Revolution: How Immutability and Virtual Threads Changed Everything!</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Wed, 31 Dec 2025 15:08:32 +0000</pubDate>
      <link>https://dev.to/dbc2201/javas-concurrency-revolution-how-immutability-and-virtual-threads-changed-everything-36kg</link>
      <guid>https://dev.to/dbc2201/javas-concurrency-revolution-how-immutability-and-virtual-threads-changed-everything-36kg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Explore how Java 25's virtual threads, Scoped Values, and the shift toward immutability are transforming concurrent programming. Learn what changed, what broke, and how to write modern, thread-safe Java code.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TL;DR — Java 25 Concurrency in 60 Seconds
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Guidance&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Virtual Threads&lt;/td&gt;
&lt;td&gt;Use for I/O-bound work. Create one virtual thread per task—do not pool them.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;synchronized&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works correctly in Java 24+. The pinning issue is resolved.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ThreadLocal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace with &lt;code&gt;ScopedValue&lt;/code&gt; (finalized in Java 25).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread Pools&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;Semaphore&lt;/code&gt; to limit resource access, not thread pool sizing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU-bound work&lt;/td&gt;
&lt;td&gt;Use parallel streams or &lt;code&gt;ForkJoinPool&lt;/code&gt;. Virtual threads provide no benefit here.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reactive (WebFlux)&lt;/td&gt;
&lt;td&gt;Keep for streaming and backpressure. Use virtual threads for request/response.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured Concurrency&lt;/td&gt;
&lt;td&gt;Preview in Java 25. Learn &lt;code&gt;StructuredTaskScope.open()&lt;/code&gt; now.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Quick Start:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Enable in Spring Boot 3.2+&lt;/span&gt;
&lt;span class="n"&gt;spring&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;threads&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;virtual&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;// Or use directly&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;blockingIOOperation&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Migration Priority:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable virtual threads in your framework&lt;/li&gt;
&lt;li&gt;Replace &lt;code&gt;ThreadLocal&lt;/code&gt; with &lt;code&gt;ScopedValue&lt;/code&gt; for context propagation&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;Semaphore&lt;/code&gt; limits for databases and external APIs&lt;/li&gt;
&lt;li&gt;Remove unnecessary &lt;code&gt;ReentrantLock&lt;/code&gt; workarounds from Java 21 era&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Terminology Used in This Article
&lt;/h2&gt;

&lt;p&gt;To avoid confusion, this article uses these terms consistently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Virtual threads&lt;/strong&gt;: Lightweight threads managed by the JVM (introduced in Java 21, enhanced through Java 25)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform threads&lt;/strong&gt;: Traditional OS-level threads (what Java used before virtual threads)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Carrier threads&lt;/strong&gt;: Platform threads that execute virtual threads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pinning&lt;/strong&gt;: When a virtual thread cannot unmount from its carrier thread during a blocking operation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;This article assumes familiarity with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic Java threading concepts (&lt;code&gt;Thread&lt;/code&gt;, &lt;code&gt;Runnable&lt;/code&gt;, &lt;code&gt;Callable&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ExecutorService&lt;/code&gt; and thread pools&lt;/li&gt;
&lt;li&gt;Fundamental concurrency concepts (race conditions, synchronization, locks)&lt;/li&gt;
&lt;li&gt;Basic understanding of reactive programming (helpful but not required)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;New to Java concurrency? Start with Oracle's &lt;a href="https://docs.oracle.com/javase/tutorial/essential/concurrency/" rel="noopener noreferrer"&gt;Concurrency Tutorial&lt;/a&gt; first.&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Remember the first time you encountered a &lt;code&gt;NullPointerException&lt;/code&gt; in production caused by a race condition? That sinking feeling when you realized your "thread-safe" code was not actually thread-safe?&lt;/p&gt;

&lt;p&gt;You are not alone. For decades, Java developers have wrestled with the complexity of concurrent programming. Here is the exciting news: Java 25 LTS fundamentally changes the game.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article covers Java 25 LTS, released September 2025. This is the current Long-Term Support release and the recommended baseline for new production deployments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  A Brief History of Java Threading
&lt;/h3&gt;

&lt;p&gt;When Java 1.0 shipped in 1995, its API contained about a hundred classes. Among them was &lt;code&gt;java.lang.Thread&lt;/code&gt;. Java became the first mainstream programming language with direct support for concurrent programming.&lt;/p&gt;

&lt;p&gt;Since Java 1.2, each Java thread runs on a &lt;strong&gt;platform thread&lt;/strong&gt; supplied by the underlying operating system. Platform threads have nontrivial costs: they require a few thousand CPU instructions to start and consume a few megabytes of memory. Server applications serving many concurrent requests quickly hit the ceiling of what platform threads can handle—especially when those requests spend most of their time &lt;em&gt;blocking&lt;/em&gt;, waiting for database results or downstream services.&lt;/p&gt;

&lt;p&gt;The classic remedy was non-blocking APIs and reactive programming. Instead of waiting for results, you register callbacks. This approach works, but the nested callbacks become unpleasant quickly.&lt;/p&gt;

&lt;p&gt;Virtual threads offer a better path. With virtual threads, blocking is cheap. When a result is not immediately available, you simply block in a virtual thread. You use familiar programming structures—branches, loops, try blocks—instead of callback pipelines.&lt;/p&gt;

&lt;p&gt;In this deep dive, we explore how Java's shift toward immutability—combined with virtual threads, Scoped Values, and Structured Concurrency—reshapes how we think about concurrent programming. Whether you maintain legacy code or start fresh, understanding this evolution is essential.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Traditional Java Concurrency Is Hard
&lt;/h2&gt;

&lt;p&gt;Traditional Java concurrency fights against how developers naturally think. Consider this familiar pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Counter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getCount&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code compiles. It runs. It even passes most tests. But it hides three problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cognitive overhead&lt;/strong&gt; — Every field access requires you to ask: "Is this synchronized?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability limits&lt;/strong&gt; — The &lt;code&gt;synchronized&lt;/code&gt; keyword serializes access, creating bottlenecks under load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging difficulty&lt;/strong&gt; — Race conditions appear intermittently. Reproducing them in test environments is often impossible.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The core mistake? Trying to make mutable state &lt;em&gt;safe&lt;/em&gt; rather than eliminating mutable state &lt;em&gt;entirely&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reflection question:&lt;/strong&gt; Think about your current codebase. How many shared mutable fields exist? How confident are you that every access is properly synchronized?&lt;/p&gt;




&lt;h2&gt;
  
  
  Java's Immutability Journey
&lt;/h2&gt;

&lt;p&gt;To address these challenges, Java's language architects embarked on a multi-year journey. Rather than adding more synchronization tools, they focused on making immutability easier to express.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timeline: Java's Concurrency Evolution
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Java Version&lt;/th&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Java 16&lt;/td&gt;
&lt;td&gt;Records&lt;/td&gt;
&lt;td&gt;Immutable data carriers by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2021&lt;/td&gt;
&lt;td&gt;Java 17&lt;/td&gt;
&lt;td&gt;Sealed Classes&lt;/td&gt;
&lt;td&gt;Constrained, predictable type hierarchies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2023&lt;/td&gt;
&lt;td&gt;Java 21&lt;/td&gt;
&lt;td&gt;Virtual Threads&lt;/td&gt;
&lt;td&gt;Lightweight threads for I/O-bound work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2024&lt;/td&gt;
&lt;td&gt;Java 24&lt;/td&gt;
&lt;td&gt;Synchronized Pinning Fix&lt;/td&gt;
&lt;td&gt;JEP 491 eliminates &lt;code&gt;synchronized&lt;/code&gt; pinning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;Java 25&lt;/td&gt;
&lt;td&gt;Scoped Values (final)&lt;/td&gt;
&lt;td&gt;Modern replacement for &lt;code&gt;ThreadLocal&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2025&lt;/td&gt;
&lt;td&gt;Java 25&lt;/td&gt;
&lt;td&gt;Structured Concurrency (preview)&lt;/td&gt;
&lt;td&gt;Task lifecycle management&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Records (Java 16)
&lt;/h3&gt;

&lt;p&gt;Records provide a concise way to create immutable data carriers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: 50+ lines of boilerplate&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getX&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getY&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// After: One line, inherently thread-safe&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Records are immutable by design. Pass them between threads without synchronization, without defensive copying, without worry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sealed Classes (Java 17)
&lt;/h3&gt;

&lt;p&gt;Sealed classes constrain inheritance. This makes type hierarchies predictable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt; 
    &lt;span class="n"&gt;permits&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Pending&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;transactionId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;errorCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Pending&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;referenceId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;PaymentResult&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you know exactly what implementations exist, reasoning about thread safety becomes tractable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter Virtual Threads
&lt;/h2&gt;

&lt;p&gt;Java 21 introduced virtual threads—lightweight threads managed by the JVM rather than the operating system. Java 25 LTS builds on this foundation with critical improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Virtual Threads Work
&lt;/h3&gt;

&lt;p&gt;Virtual threads decouple the concept of a "thread" from OS resources:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your code runs on a &lt;strong&gt;virtual thread&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The JVM schedules virtual threads onto &lt;strong&gt;carrier threads&lt;/strong&gt; (platform threads)&lt;/li&gt;
&lt;li&gt;When a virtual thread blocks (I/O, sleep, lock), it &lt;strong&gt;unmounts&lt;/strong&gt; from its carrier&lt;/li&gt;
&lt;li&gt;The carrier can then run another virtual thread&lt;/li&gt;
&lt;li&gt;When the blocking operation completes, the virtual thread &lt;strong&gt;mounts&lt;/strong&gt; again (possibly on a different carrier)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Old approach: Carefully sized thread pools&lt;/span&gt;
&lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Hit this limit and requests queue or get rejected&lt;/span&gt;

&lt;span class="c1"&gt;// New approach: Spawn threads freely&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; 
        &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;processTask&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// One million concurrent tasks? No problem.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Performance Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Platform Threads&lt;/th&gt;
&lt;th&gt;Virtual Threads&lt;/th&gt;
&lt;th&gt;Improvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;10K concurrent HTTP calls&lt;/td&gt;
&lt;td&gt;45 sec&lt;/td&gt;
&lt;td&gt;3.2 sec&lt;/td&gt;
&lt;td&gt;14x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory footprint (10K threads)&lt;/td&gt;
&lt;td&gt;~10 GB&lt;/td&gt;
&lt;td&gt;~200 MB&lt;/td&gt;
&lt;td&gt;50x less&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread creation time (1K threads)&lt;/td&gt;
&lt;td&gt;1,200 ms&lt;/td&gt;
&lt;td&gt;12 ms&lt;/td&gt;
&lt;td&gt;100x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Max concurrent connections&lt;/td&gt;
&lt;td&gt;~4,000&lt;/td&gt;
&lt;td&gt;~1,000,000+&lt;/td&gt;
&lt;td&gt;250x more&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Benchmark note:&lt;/strong&gt; These numbers represent typical results. Your mileage will vary based on workload, hardware, and JVM configuration. Always benchmark your specific use case.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Creating Virtual Threads: All the Options
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Executors.newVirtualThreadPerTaskExecutor()&lt;/code&gt; factory is the most common approach, but Java provides several ways to create virtual threads:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Virtual Thread Per Task Executor (recommended for most cases)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchData&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 2: Thread.Builder for Custom Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;Thread.Builder&lt;/code&gt; when you need named threads or a thread factory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a builder with auto-incrementing names&lt;/span&gt;
&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofVirtual&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"request-handler-"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create unstarted thread&lt;/span&gt;
&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;t1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unstarted&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;t1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Or create and start immediately&lt;/span&gt;
&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;t2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;started&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Get a factory for use with other APIs&lt;/span&gt;
&lt;span class="nc"&gt;ThreadFactory&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;factory&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 3: Quick One-Off Thread&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For demos or simple cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startVirtualThread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doWork&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option 4: invokeAll for Multiple Tasks with Same Result Type&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you have a list of similar tasks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Callable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;invokeAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Thread API Behavioral Differences
&lt;/h3&gt;

&lt;p&gt;A virtual thread is an instance of &lt;code&gt;Thread&lt;/code&gt;, but it behaves differently in several ways:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Platform Threads&lt;/th&gt;
&lt;th&gt;Virtual Threads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Thread group&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;Single fixed group (cannot change)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Priority&lt;/td&gt;
&lt;td&gt;Configurable (1-10)&lt;/td&gt;
&lt;td&gt;Always &lt;code&gt;NORM_PRIORITY&lt;/code&gt; (5)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daemon status&lt;/td&gt;
&lt;td&gt;Configurable&lt;/td&gt;
&lt;td&gt;Always daemon (cannot change)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getAllStackTraces()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Included&lt;/td&gt;
&lt;td&gt;Not included&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code&gt;setPriority()&lt;/code&gt; or &lt;code&gt;setDaemon()&lt;/code&gt; on a virtual thread has no effect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New and Changed Methods:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Check if thread is virtual&lt;/span&gt;
&lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;isVirtual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isVirtual&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Duration-based methods (added in Java 19)&lt;/span&gt;
&lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofMillis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Use threadId() instead of deprecated getId()&lt;/span&gt;
&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;threadId&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Removed Methods (throw UnsupportedOperationException):&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;stop()&lt;/code&gt;, &lt;code&gt;suspend()&lt;/code&gt;, and &lt;code&gt;resume()&lt;/code&gt; methods throw &lt;code&gt;UnsupportedOperationException&lt;/code&gt; for both platform and virtual threads as of Java 20. These methods were deprecated since Java 1.2.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use &lt;code&gt;LockSupport.parkNanos()&lt;/code&gt; instead of &lt;code&gt;Thread.sleep()&lt;/code&gt; when you want to avoid catching &lt;code&gt;InterruptedException&lt;/code&gt;:&lt;/p&gt;


&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;LockSupport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parkNanos&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000_000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Sleep 1 second, no checked exception&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  JVM Tuning Options
&lt;/h3&gt;

&lt;p&gt;Control virtual thread behavior with these JVM options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jdk.virtualThreadScheduler.parallelism&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CPU cores&lt;/td&gt;
&lt;td&gt;Number of carrier threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jdk.virtualThreadScheduler.maxPoolSize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;256&lt;/td&gt;
&lt;td&gt;Maximum carrier threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jdk.tracePinnedThreads=short&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Print brief pinning warnings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jdk.tracePinnedThreads=full&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Print full stack traces for pinning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jdk.traceVirtualThreadLocals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Trace ThreadLocal mutations in virtual threads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Example startup command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;-Djdk&lt;/span&gt;.virtualThreadScheduler.parallelism&lt;span class="o"&gt;=&lt;/span&gt;16 &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-Djdk&lt;/span&gt;.tracePinnedThreads&lt;span class="o"&gt;=&lt;/span&gt;short &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-jar&lt;/span&gt; myapp.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code&gt;jdk.tracePinnedThreads&lt;/code&gt; option prints only one warning per pinning location in your code. Use JFR for comprehensive pinning analysis.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Common Pitfalls to Avoid
&lt;/h2&gt;

&lt;p&gt;Before diving deeper, learn from these common mistakes. Avoiding them early saves significant debugging time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall 1: Pooling Virtual Threads
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: Do not pool virtual threads&lt;/span&gt;
&lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startVirtualThread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doWork&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT: One virtual thread per task&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;doWork&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why pooling is wrong:&lt;/strong&gt; Scheduling tasks on virtual threads that are then scheduled on platform threads is clearly inefficient—you add overhead without benefit. And what is the upside? To limit virtual threads to a small number of concurrent requests? Then why use virtual threads at all?&lt;/p&gt;

&lt;p&gt;With platform threads, pool sizing was a crude but effective tuning knob. With virtual threads, use &lt;code&gt;Semaphore&lt;/code&gt; or other mechanisms to protect specific limited resources instead of capping overall concurrency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall 2: Using ThreadLocal for Caching
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: Creates millions of cached objects&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withInitial&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;ObjectMapper:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT: Use a shared immutable instance&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt; &lt;span class="no"&gt;MAPPER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ObjectMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ALTERNATIVE: Use a bounded pool for mutable objects&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ObjectPool&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StringBuilder&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;POOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GenericObjectPool&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StringBuilderFactory&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each virtual thread gets its own &lt;code&gt;ThreadLocal&lt;/code&gt; copy. With millions of virtual threads, you exhaust heap memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall 3: Forgetting Resource Limits
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: Overwhelms the database&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT: Limit concurrent access with Semaphore&lt;/span&gt;
&lt;span class="nc"&gt;Semaphore&lt;/span&gt; &lt;span class="n"&gt;dbPermits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Semaphore&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Match connection pool size&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;dbPermits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;acquire&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;dbPermits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;interrupt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Interrupted while waiting for permit"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Virtual threads remove JVM limits. External resources (databases, APIs, file handles) still have limits.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall 4: Expecting CPU-Bound Speedup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: Virtual threads provide no benefit here&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;computePrimes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT: Use parallel streams or ForkJoinPool for CPU work&lt;/span&gt;
&lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parallel&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;computePrimes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Virtual threads optimize for blocking I/O. CPU-bound tasks gain nothing and may perform worse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall 5: Mixing Blocking and Reactive Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: Blocking inside reactive pipeline&lt;/span&gt;
&lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromCallable&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;blockingHttpCall&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Blocks reactive thread!&lt;/span&gt;
    &lt;span class="o"&gt;}))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// CORRECT: Choose one paradigm&lt;/span&gt;
&lt;span class="c1"&gt;// Option A: Full reactive&lt;/span&gt;
&lt;span class="nc"&gt;Flux&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;webClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retrieve&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bodyToMono&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Option B: Full virtual threads&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;IntStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;blockingHttpCall&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Blocking calls starve the reactive scheduler's limited thread pool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pitfall Summary
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pitfall&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pooling virtual threads&lt;/td&gt;
&lt;td&gt;No performance gain&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;newVirtualThreadPerTaskExecutor()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;ThreadLocal&lt;/code&gt; caching&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OutOfMemoryError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use shared immutable or bounded pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No resource limits&lt;/td&gt;
&lt;td&gt;Connection exhaustion&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;Semaphore&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU-bound tasks&lt;/td&gt;
&lt;td&gt;High CPU, no speedup&lt;/td&gt;
&lt;td&gt;Use parallel streams&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mixed paradigms&lt;/td&gt;
&lt;td&gt;Thread starvation&lt;/td&gt;
&lt;td&gt;Commit to one approach&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Reflection question:&lt;/strong&gt; Review your current thread pool configurations. Are they sized for JVM limits or for external resource limits?&lt;/p&gt;




&lt;h2&gt;
  
  
  Java 24+ Update: Synchronized Pinning Is Fixed
&lt;/h2&gt;

&lt;p&gt;If you learned about virtual threads from earlier articles or Java 21 guides, note this important change: JEP 491 (Java 24) eliminated the &lt;code&gt;synchronized&lt;/code&gt; pinning problem. This improvement carries forward to Java 25 LTS.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Was the Problem?
&lt;/h3&gt;

&lt;p&gt;In Java 21-23, when a virtual thread entered a &lt;code&gt;synchronized&lt;/code&gt; block and performed a blocking operation, it became "pinned" to its carrier platform thread. This negated scalability benefits.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Java 21-23 Compensatory Behavior:&lt;/strong&gt; In those versions, the virtual thread scheduler would sometimes compensate by starting additional carrier threads. This happened for many file I/O operations and when calling &lt;code&gt;Object.wait()&lt;/code&gt;. However, this compensation had limits controlled by &lt;code&gt;jdk.virtualThreadScheduler.maxPoolSize&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What Changed?
&lt;/h3&gt;

&lt;p&gt;The JVM now allows virtual threads to acquire, hold, and release monitors independently of their carrier threads. This is a JVM-level optimization—no code changes required.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is now fine in Java 24+ / Java 25 LTS&lt;/span&gt;
&lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;performBlockingIO&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Virtual thread can unmount!&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You no longer need to reflexively replace &lt;code&gt;synchronized&lt;/code&gt; with &lt;code&gt;ReentrantLock&lt;/code&gt; for virtual thread compatibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  When ReentrantLock Is Still Appropriate
&lt;/h3&gt;

&lt;p&gt;Pinning still occurs when virtual threads call native code via:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native methods (JNI)&lt;/li&gt;
&lt;li&gt;Foreign Function &amp;amp; Memory API callbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these edge cases, &lt;code&gt;ReentrantLock&lt;/code&gt; remains appropriate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReentrantLock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;callNative&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;callNativeLibrary&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Pinning would occur with synchronized&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose &lt;code&gt;ReentrantLock&lt;/code&gt; when you need: &lt;code&gt;tryLock()&lt;/code&gt;, timed waits, interruptible locking, or native code interactions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scoped Values: The ThreadLocal Replacement
&lt;/h2&gt;

&lt;p&gt;Java 25 finalizes Scoped Values (JEP 506)—a modern replacement for &lt;code&gt;ThreadLocal&lt;/code&gt; designed for virtual threads.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem with ThreadLocal
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Creates one formatter per virtual thread (potentially millions!)&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="nc"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withInitial&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleDateFormat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problems with this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Each virtual thread gets its own copy&lt;/li&gt;
&lt;li&gt;Values are mutable—any code can call &lt;code&gt;set()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Lifetime is unbounded—you must manually call &lt;code&gt;remove()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Memory leaks occur if cleanup is missed&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Debugging tip:&lt;/strong&gt; To locate ThreadLocal usage in your application, run with &lt;code&gt;-Djdk.traceVirtualThreadLocals&lt;/code&gt;. You will get a stack trace whenever a virtual thread mutates a thread-local variable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Scoped Values Solution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RequestContext&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;REQUEST_CTX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
    &lt;span class="nc"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newInstance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RequestContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;traceId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="nc"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;REQUEST_CTX&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;processRequest&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;       &lt;span class="c1"&gt;// Can read REQUEST_CTX.get()&lt;/span&gt;
        &lt;span class="n"&gt;callDownstreamService&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Inherited automatically&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// Value automatically cleared when scope exits&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processRequest&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;REQUEST_CTX&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Access anywhere in call stack&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing request: {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;traceId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Comparison: ThreadLocal vs Scoped Values
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ThreadLocal&lt;/th&gt;
&lt;th&gt;Scoped Values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mutability&lt;/td&gt;
&lt;td&gt;Mutable (set/get)&lt;/td&gt;
&lt;td&gt;Immutable bindings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lifetime&lt;/td&gt;
&lt;td&gt;Unbounded (manual cleanup)&lt;/td&gt;
&lt;td&gt;Scoped (automatic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Virtual thread cost&lt;/td&gt;
&lt;td&gt;High (per-thread copy)&lt;/td&gt;
&lt;td&gt;Low (inherited)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory leaks&lt;/td&gt;
&lt;td&gt;Common&lt;/td&gt;
&lt;td&gt;Impossible by design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inheritance&lt;/td&gt;
&lt;td&gt;Expensive copying&lt;/td&gt;
&lt;td&gt;Zero-cost sharing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Structured Concurrency (Preview in Java 25)
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Structured Concurrency is a preview feature in Java 25. Enable with &lt;code&gt;--enable-preview&lt;/code&gt;. The API may change in future releases.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Java 25 previews Structured Concurrency (JEP 505) with a significantly revised API. While still in preview, it represents the future of concurrent task management.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Pattern
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Enable preview: --enable-preview&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Subtask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="nc"&gt;Subtask&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchOrders&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Wait for all tasks&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UserDashboard&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Scope closes: all subtasks guaranteed complete or cancelled&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern eliminates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread leaks&lt;/li&gt;
&lt;li&gt;Orphaned tasks&lt;/li&gt;
&lt;li&gt;Complex error propagation logic&lt;/li&gt;
&lt;li&gt;Manual executor management&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using Joiners for Different Policies
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// All must succeed—returns stream of results&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Joiner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;allSuccessfulOrThrow&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchFromServiceA&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchFromServiceB&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;fetchFromServiceC&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// First success wins (race pattern)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nc"&gt;Joiner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anySuccessfulResultOrThrow&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;queryPrimaryDB&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;queryReplicaDB&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Returns first successful result&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  API Changes from Java 21-24
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Java 21-24&lt;/th&gt;
&lt;th&gt;Java 25&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Creation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;new StructuredTaskScope&amp;lt;&amp;gt;()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StructuredTaskScope.open()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policies&lt;/td&gt;
&lt;td&gt;Subclasses (ShutdownOnFailure)&lt;/td&gt;
&lt;td&gt;Joiner objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Join result&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;void&lt;/code&gt; + &lt;code&gt;throwIfFailed()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Returns result directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Type&lt;/td&gt;
&lt;td&gt;Class&lt;/td&gt;
&lt;td&gt;Sealed interface&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Framework Integration Guide
&lt;/h2&gt;

&lt;p&gt;Most Java applications use frameworks. Here is how to enable virtual threads in popular frameworks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spring Boot 3.2+
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application.yml&lt;/span&gt;
&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;threads&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;virtual&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or configure programmatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VirtualThreadConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;TomcatProtocolHandlerCustomizer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;virtualThreadCustomizer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;protocolHandler&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;protocolHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setExecutor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;};&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;AsyncTaskExecutor&lt;/span&gt; &lt;span class="nf"&gt;applicationTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;TaskExecutorAdapter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Quarkus 3.x
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# application.properties
&lt;/span&gt;&lt;span class="py"&gt;quarkus.virtual-threads.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/orders"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderResource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;
    &lt;span class="nd"&gt;@RunOnVirtualThread&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getOrders&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Blocking call is fine&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Micronaut 4.x
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application.yml&lt;/span&gt;
&lt;span class="na"&gt;micronaut&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;thread-selection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AUTO&lt;/span&gt;
  &lt;span class="na"&gt;executors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;virtual&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Controller&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/orders"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Get&lt;/span&gt;
    &lt;span class="nd"&gt;@ExecuteOn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TaskExecutors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;VIRTUAL&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getOrders&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Database Driver Compatibility
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Driver&lt;/th&gt;
&lt;th&gt;Virtual Thread Ready&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL (pgjdbc 42.6+)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Fully compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MySQL Connector/J 8.1+&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Fully compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle JDBC 23c+&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Fully compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HikariCP 5.1+&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Use with semaphore limiting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MongoDB Java Driver 4.11+&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Async driver also available&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  HTTP Client Compatibility
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client&lt;/th&gt;
&lt;th&gt;Virtual Thread Ready&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;java.net.http.HttpClient&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Built-in, preferred&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apache HttpClient 5.3+&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Classic API compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OkHttp 4.12+&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Fully compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spring RestClient (6.1+)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;New blocking client&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Real-World Migration: A Case Study
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Scenario
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Application&lt;/strong&gt;: Order Processing Service&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;50+ REST endpoints&lt;/li&gt;
&lt;li&gt;PostgreSQL database&lt;/li&gt;
&lt;li&gt;Redis cache&lt;/li&gt;
&lt;li&gt;3 downstream microservice dependencies&lt;/li&gt;
&lt;li&gt;Peak load: 5,000 requests/second&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Before: Reactive WebFlux
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;OrderResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;validateOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserId&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inventoryService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;checkStock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAvailable&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OutOfStockException&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTotal&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
            &lt;span class="o"&gt;}))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OrderResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"SUCCESS"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onErrorResume&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OutOfStockException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Mono&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;just&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OrderResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OUT_OF_STOCK"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  After: Virtual Threads
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;OrderResponse&lt;/span&gt; &lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;validateOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUserId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="nc"&gt;StockStatus&lt;/span&gt; &lt;span class="n"&gt;stock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inventoryService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;checkStock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;stock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isAvailable&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"OUT_OF_STOCK"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;Payment&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;processPayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTotal&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"SUCCESS"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OrderResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"PAYMENT_FAILED"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Migration Results
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before (WebFlux)&lt;/th&gt;
&lt;th&gt;After (Virtual Threads)&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lines of code&lt;/td&gt;
&lt;td&gt;12,450&lt;/td&gt;
&lt;td&gt;7,470&lt;/td&gt;
&lt;td&gt;-40%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Avg debugging time&lt;/td&gt;
&lt;td&gt;45 min&lt;/td&gt;
&lt;td&gt;18 min&lt;/td&gt;
&lt;td&gt;-60%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New developer onboarding&lt;/td&gt;
&lt;td&gt;2 weeks&lt;/td&gt;
&lt;td&gt;3 days&lt;/td&gt;
&lt;td&gt;-78%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;P99 latency&lt;/td&gt;
&lt;td&gt;142 ms&lt;/td&gt;
&lt;td&gt;138 ms&lt;/td&gt;
&lt;td&gt;-3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory usage&lt;/td&gt;
&lt;td&gt;512 MB&lt;/td&gt;
&lt;td&gt;384 MB&lt;/td&gt;
&lt;td&gt;-25%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Key Lessons
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Start with non-critical paths—migrate background jobs and internal APIs first&lt;/li&gt;
&lt;li&gt;Keep reactive for streaming—WebSocket handlers stayed on WebFlux&lt;/li&gt;
&lt;li&gt;Update dependencies—ensure JDBC drivers and HTTP clients support virtual threads&lt;/li&gt;
&lt;li&gt;Monitor pinning events—use JFR to identify unexpected pinning&lt;/li&gt;
&lt;li&gt;Train the team—one day workshop was sufficient&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The migration took 6 weeks for a team of 4 developers. The codebase became dramatically simpler.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging and Observability
&lt;/h2&gt;

&lt;p&gt;Virtual threads change how you debug and monitor applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Detecting Pinning with JFR
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start recording with pinning detection&lt;/span&gt;
java &lt;span class="nt"&gt;-XX&lt;/span&gt;:StartFlightRecording&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;recording.jfr,settings&lt;span class="o"&gt;=&lt;/span&gt;profile &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-Djdk&lt;/span&gt;.tracePinnedThreads&lt;span class="o"&gt;=&lt;/span&gt;full &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-jar&lt;/span&gt; myapp.jar

&lt;span class="c"&gt;# Analyze with JDK Mission Control&lt;/span&gt;
jfr print &lt;span class="nt"&gt;--events&lt;/span&gt; jdk.VirtualThreadPinned recording.jfr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using &lt;code&gt;-Djdk.tracePinnedThreads&lt;/code&gt;, you get stack traces showing where pinning occurs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) &amp;lt;== monitors:1
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The &lt;code&gt;-Djdk.tracePinnedThreads&lt;/code&gt; option prints only one warning per pinning location in your code. For comprehensive analysis, use Java Flight Recorder and look for &lt;code&gt;VirtualThreadPinned&lt;/code&gt; and &lt;code&gt;VirtualThreadSubmitFailed&lt;/code&gt; events.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Key JFR Events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;When to Monitor&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jdk.VirtualThreadStart&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thread creation&lt;/td&gt;
&lt;td&gt;Unusual spawn patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jdk.VirtualThreadEnd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Thread completion&lt;/td&gt;
&lt;td&gt;Lifecycle issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jdk.VirtualThreadPinned&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Carrier thread blocked&lt;/td&gt;
&lt;td&gt;Performance issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;jdk.VirtualThreadSubmitFailed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scheduler rejection&lt;/td&gt;
&lt;td&gt;Overload detection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Thread Dumps
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# JSON format shows structured concurrency hierarchy&lt;/span&gt;
jcmd &amp;lt;pid&amp;gt; Thread.dump_to_file &lt;span class="nt"&gt;-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json threads.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Troubleshooting Checklist
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely Cause&lt;/th&gt;
&lt;th&gt;Investigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;High latency under load&lt;/td&gt;
&lt;td&gt;Pinning&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;jdk.VirtualThreadPinned&lt;/code&gt; events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OutOfMemoryError&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ThreadLocal abuse&lt;/td&gt;
&lt;td&gt;Profile heap for thread-local objects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection timeouts&lt;/td&gt;
&lt;td&gt;Resource exhaustion&lt;/td&gt;
&lt;td&gt;Verify semaphore limits match pool sizes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stack traces missing frames&lt;/td&gt;
&lt;td&gt;Async boundaries&lt;/td&gt;
&lt;td&gt;Enable async stack trace in IDE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Decision Guide: When to Use What
&lt;/h2&gt;

&lt;p&gt;Use this consolidated guide to choose the right approach for your use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtual Threads vs Reactive vs Platform Threads
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;REST API handlers&lt;/td&gt;
&lt;td&gt;Virtual Threads&lt;/td&gt;
&lt;td&gt;Simple, readable blocking code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database queries&lt;/td&gt;
&lt;td&gt;Virtual Threads&lt;/td&gt;
&lt;td&gt;Natural JDBC usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP client calls&lt;/td&gt;
&lt;td&gt;Virtual Threads&lt;/td&gt;
&lt;td&gt;Straightforward error handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSocket streaming&lt;/td&gt;
&lt;td&gt;Reactive&lt;/td&gt;
&lt;td&gt;Backpressure support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kafka consumers&lt;/td&gt;
&lt;td&gt;Reactive or Structured Concurrency&lt;/td&gt;
&lt;td&gt;Event stream processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Background jobs&lt;/td&gt;
&lt;td&gt;Virtual Threads&lt;/td&gt;
&lt;td&gt;One thread per job&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time analytics&lt;/td&gt;
&lt;td&gt;Reactive&lt;/td&gt;
&lt;td&gt;Complex event processing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU-intensive computation&lt;/td&gt;
&lt;td&gt;Platform Threads (ForkJoinPool)&lt;/td&gt;
&lt;td&gt;No I/O blocking benefit&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  synchronized vs ReentrantLock
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple mutual exclusion&lt;/td&gt;
&lt;td&gt;&lt;code&gt;synchronized&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need &lt;code&gt;tryLock()&lt;/code&gt; or timeout&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ReentrantLock&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Need interruptible locking&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ReentrantLock&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native code interactions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ReentrantLock&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maximum simplicity&lt;/td&gt;
&lt;td&gt;&lt;code&gt;synchronized&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  ThreadLocal vs ScopedValue
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request context propagation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ScopedValue&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transaction context&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ScopedValue&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mutable per-thread cache&lt;/td&gt;
&lt;td&gt;Avoid; use shared immutable or bounded pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy library integration&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ThreadLocal&lt;/code&gt; (if required by library)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Testing Virtual Thread Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic Unit Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shouldRunOnVirtualThread&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startVirtualThread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isVirtual&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;assertTrue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Concurrency Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shouldHandleConcurrentAccess&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;taskCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;futures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;taskCount&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;counter:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;incrementAndGet&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;futures&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;taskCount&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;counter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scoped Values Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;USER_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newInstance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;shouldPropagateScopedValues&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;capturedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicReference&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;USER_ID&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"test-user-123"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test-user-123"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;USER_ID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;capturedValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;USER_ID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;});&lt;/span&gt;
            &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;});&lt;/span&gt;

    &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test-user-123"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capturedValue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Your Action Plan
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Embrace Immutability First
&lt;/h3&gt;

&lt;p&gt;Before touching threads, make your data structures immutable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Convert DTOs to Records&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;List.of()&lt;/code&gt;, &lt;code&gt;Map.of()&lt;/code&gt; for collections&lt;/li&gt;
&lt;li&gt;Replace setters with "wither" methods or builders&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Audit Your ThreadLocal Usage
&lt;/h3&gt;

&lt;p&gt;Search your codebase for &lt;code&gt;ThreadLocal&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context propagation → Migrate to Scoped Values&lt;/li&gt;
&lt;li&gt;Object caching → Consider immutable alternatives or proper pools&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Review Synchronization
&lt;/h3&gt;

&lt;p&gt;In Java 25, &lt;code&gt;synchronized&lt;/code&gt; works fine with virtual threads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove unnecessary &lt;code&gt;ReentrantLock&lt;/code&gt; migrations done for Java 21&lt;/li&gt;
&lt;li&gt;Keep &lt;code&gt;ReentrantLock&lt;/code&gt; where you need its advanced features&lt;/li&gt;
&lt;li&gt;Watch for native code interactions (still causes pinning)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 4: Experiment with Virtual Threads
&lt;/h3&gt;

&lt;p&gt;Start small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startVirtualThread&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Your existing blocking code works here&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Learn Structured Concurrency
&lt;/h3&gt;

&lt;p&gt;Even as a preview feature, understanding this pattern prepares you for Java's future:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Enable preview: --enable-preview&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StructuredTaskScope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;open&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;task1&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fork&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;combine&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;result2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Immutability is the foundation&lt;/strong&gt; — Records and sealed classes make thread safety the default&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual threads change the economics&lt;/strong&gt; — Blocking is cheap; shared state is expensive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;synchronized works again&lt;/strong&gt; — Java 24+ fixed the pinning issue&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scoped Values replace ThreadLocal&lt;/strong&gt; — Finalized in Java 25, designed for virtual threads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thread pools are for bounding, not scaling&lt;/strong&gt; — Use semaphores for resource limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactive has its place&lt;/strong&gt; — Streaming and backpressure, not request/response&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured Concurrency is the future&lt;/strong&gt; — Preview in Java 25, learn it now&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Practical Summary
&lt;/h3&gt;

&lt;p&gt;Use virtual threads when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have many tasks that mostly block on network I/O&lt;/li&gt;
&lt;li&gt;You want the familiar "synchronous" programming style without callbacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not use virtual threads for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU-intensive computation (use parallel streams or &lt;code&gt;ForkJoinPool&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Tasks that require thread affinity or native code interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do not pool virtual threads—use other mechanisms for rate limiting&lt;/li&gt;
&lt;li&gt;Check for pinning with &lt;code&gt;-Djdk.tracePinnedThreads&lt;/code&gt; and mitigate if necessary&lt;/li&gt;
&lt;li&gt;Minimize thread-local variables; prefer &lt;code&gt;ScopedValue&lt;/code&gt; for context propagation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Version History and Compatibility
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Introduced&lt;/th&gt;
&lt;th&gt;Finalized&lt;/th&gt;
&lt;th&gt;Status in Java 25&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Records&lt;/td&gt;
&lt;td&gt;Java 14 (preview)&lt;/td&gt;
&lt;td&gt;Java 16&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sealed Classes&lt;/td&gt;
&lt;td&gt;Java 15 (preview)&lt;/td&gt;
&lt;td&gt;Java 17&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Virtual Threads&lt;/td&gt;
&lt;td&gt;Java 19 (preview)&lt;/td&gt;
&lt;td&gt;Java 21&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Synchronized Pinning Fix&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Java 24&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scoped Values&lt;/td&gt;
&lt;td&gt;Java 20 (incubator)&lt;/td&gt;
&lt;td&gt;Java 25&lt;/td&gt;
&lt;td&gt;Stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured Concurrency&lt;/td&gt;
&lt;td&gt;Java 19 (incubator)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Preview (JEP 505)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Further Reading and Resources
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Official Documentation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.java/learn/new-features/virtual-threads/" rel="noopener noreferrer"&gt;Virtual Threads - dev.java&lt;/a&gt; — Cay Horstmann's comprehensive tutorial&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openjdk.org/jeps/444" rel="noopener noreferrer"&gt;JEP 444: Virtual Threads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openjdk.org/jeps/491" rel="noopener noreferrer"&gt;JEP 491: Synchronize Virtual Threads without Pinning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openjdk.org/jeps/506" rel="noopener noreferrer"&gt;JEP 506: Scoped Values&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://openjdk.org/jeps/505" rel="noopener noreferrer"&gt;JEP 505: Structured Concurrency (Fifth Preview)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/en/java/javase/25/core/virtual-threads.html" rel="noopener noreferrer"&gt;Oracle Virtual Threads Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Videos and Talks
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=vLJDPmXufQw" rel="noopener noreferrer"&gt;Inside Java Newscast #91: Structured Concurrency Revamp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=duIceCXObrA" rel="noopener noreferrer"&gt;Java 25 Launch Event&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=5E0LU85EnTI" rel="noopener noreferrer"&gt;Virtual Threads: A Practical Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jdk.java.net/jmc/" rel="noopener noreferrer"&gt;JDK Mission Control&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://visualvm.github.io/" rel="noopener noreferrer"&gt;VisualVM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/async-profiler/async-profiler" rel="noopener noreferrer"&gt;async-profiler&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  About the Author
&lt;/h2&gt;

&lt;p&gt;I am a Corporate and Technical Trainer specializing in full-stack development, helping engineering students and professionals navigate the software engineering landscape. Connect with me to discuss training programs or to discuss Java.&lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Beyond the One-Way Mirror: Making Tech Interviews Truly Mutual</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Mon, 14 Jul 2025 06:25:05 +0000</pubDate>
      <link>https://dev.to/dbc2201/beyond-the-one-way-mirror-making-tech-interviews-truly-mutual-g0</link>
      <guid>https://dev.to/dbc2201/beyond-the-one-way-mirror-making-tech-interviews-truly-mutual-g0</guid>
      <description>&lt;p&gt;As developers, we've all experienced that familiar interview dynamic: sitting across from someone with a clipboard, answering question after question, while rarely getting the chance to understand the environment we might be joining. It's natural to feel like you're being evaluated without getting to assess in return. But what if we reframed this entirely?&lt;/p&gt;

&lt;p&gt;The truth is, interviews &lt;em&gt;should&lt;/em&gt; be mutual assessments. You're not just seeking any job—you're looking for the right fit for your career, growth, and daily work life. And the companies interviewing you? They genuinely want to find someone who'll thrive in their environment, not just survive it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Current Reality
&lt;/h2&gt;

&lt;p&gt;Before we dive into solutions, let's acknowledge why interviews often feel one-sided. Many interviewers—even technical ones—are working within constraints we don't always see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HR professionals may lack deep technical knowledge, but are skilled at assessing cultural fit and communication&lt;/li&gt;
&lt;li&gt;Technical team members might be juggling interviews with their regular workload, relying on standardised questions for consistency&lt;/li&gt;
&lt;li&gt;Companies often prioritise legal compliance and fairness, leading to more structured approaches&lt;/li&gt;
&lt;li&gt;Time constraints mean interviewers focus on what they consider most predictive of success&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This doesn't excuse poor interview experiences, but understanding these realities helps us approach the situation more constructively.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Case for Mutual Assessment
&lt;/h2&gt;

&lt;p&gt;When you ask thoughtful questions during interviews, you're not being confrontational—you're being professional. Consider these benefits:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For You:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gain insight into technical challenges you'd face&lt;/li&gt;
&lt;li&gt;Understand team dynamics and growth opportunities&lt;/li&gt;
&lt;li&gt;Assess whether the role aligns with your career trajectory&lt;/li&gt;
&lt;li&gt;Demonstrate your analytical thinking and genuine interest&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Them:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Attract candidates who care about fit, not just employment&lt;/li&gt;
&lt;li&gt;Showcase their technical strengths and culture&lt;/li&gt;
&lt;li&gt;Identify people who'll be engaged and committed&lt;/li&gt;
&lt;li&gt;Improve their interview process through feedback&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Strategic Questions That Create Dialogue
&lt;/h2&gt;

&lt;p&gt;The key is asking questions that invite conversation rather than create tests. Here are approaches that work:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For Understanding Technical Environment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What's a recent technical challenge your team tackled, and how did you approach it?"&lt;/li&gt;
&lt;li&gt;"How do you balance maintaining existing systems with building new features?"&lt;/li&gt;
&lt;li&gt;"What does your code review process look like?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Assessing Team Dynamics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"How do team members typically collaborate on complex projects?"&lt;/li&gt;
&lt;li&gt;"What does professional development look like here?"&lt;/li&gt;
&lt;li&gt;"How do you handle knowledge sharing across the team?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Gauging Company Maturity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What's your approach to technical debt?"&lt;/li&gt;
&lt;li&gt;"How do you evaluate and adopt new technologies?"&lt;/li&gt;
&lt;li&gt;"What's been your team's biggest learning from a project that didn't go as planned?"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reading Between the Lines
&lt;/h2&gt;

&lt;p&gt;Pay attention to how questions are received and answered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Engagement vs. Evasion:&lt;/strong&gt; Strong teams welcome technical discussions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specific Examples:&lt;/strong&gt; Concrete stories indicate genuine experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comfort with Challenges:&lt;/strong&gt; Openness about difficulties shows maturity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; Aligned answers across interviewers suggest good communication&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Addressing Concerns Constructively
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"What if they see me as difficult?"&lt;/strong&gt;&lt;br&gt;
Frame your questions as wanting to understand the environment, not testing them. Most technical professionals appreciate candidates who think deeply about fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What if they can't answer?"&lt;/strong&gt;&lt;br&gt;
That's valuable information. It might indicate gaps in technical leadership or communication between teams.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What if it backfires?"&lt;/strong&gt;&lt;br&gt;
Companies that respond poorly to thoughtful questions might not be places where you'd thrive anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Balanced Perspective
&lt;/h2&gt;

&lt;p&gt;This approach isn't about "getting back" at anyone or exposing incompetence. It's about creating more authentic, productive conversations that help both parties make better decisions. &lt;/p&gt;

&lt;p&gt;Some interviewers might initially be caught off-guard, but this can lead to more genuine discussions about the real work you'd be doing. Others might appreciate the opportunity to showcase their technical environment and team culture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;When more candidates engage in thoughtful dialogue during interviews, it naturally elevates the entire process. Companies begin to see the value in having technically knowledgeable people involved in hiring. HR teams develop better questions and partner more effectively with technical staff. The result is better matches and more satisfied employees on both sides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Forward
&lt;/h2&gt;

&lt;p&gt;Your interview experience is ultimately about finding a mutual fit. By approaching it as a professional conversation rather than an interrogation, you're more likely to find environments where you'll truly thrive. And in the process, you're contributing to a more mature, respectful hiring culture that benefits everyone in tech.&lt;/p&gt;

&lt;p&gt;The next time you're in an interview, remember: you're not just being evaluated—you're being considered too. That's not adversarial; it's professional. Ask your questions, listen carefully, and trust that the right opportunities will welcome your engagement.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>interview</category>
      <category>career</category>
    </item>
    <item>
      <title>Breaking the LeetCode Loop: How Goodhart’s Law Exposes the Pitfalls of Mindless Coding Grinds</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Sat, 22 Mar 2025 14:35:04 +0000</pubDate>
      <link>https://dev.to/dbc2201/breaking-the-leetcode-loop-how-goodharts-law-exposes-the-pitfalls-of-mindless-coding-grinds-26hg</link>
      <guid>https://dev.to/dbc2201/breaking-the-leetcode-loop-how-goodharts-law-exposes-the-pitfalls-of-mindless-coding-grinds-26hg</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction: The Coding Olympiad Obsession&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Walk into any computer science department today, and you’ll hear the same refrain: &lt;em&gt;“How many LeetCode problems have you solved this week?”&lt;/em&gt; Undergraduate students worldwide grind algorithmic puzzles day and night, treating coding platforms like digital battlefields. But while mastering algorithms is valuable, this singular focus has spiralled into a counterproductive obsession—one that Goodhart’s Law helps explain, and one that risks leaving students unprepared for real-world software development.  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;What Is Goodhart’s Law? (And Why Should You Care?)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Coined by economist Charles Goodhart, the principle states:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“When a measure becomes a target, it ceases to be a good measure.”&lt;/strong&gt;  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In simpler terms: Once we optimize for a specific metric (like LeetCode problem counts), we distort its original purpose. Students aren’t alone in this trap—companies like FAANG once prioritized algorithmic interviews but now report frustration with hires who lack practical skills like debugging or API design.  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;The LeetCode Bandwagon: A Case Study in Misaligned Incentives&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The modern CS student’s reality:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fear-Driven Practice&lt;/strong&gt;: “If I don’t solve 500 problems, I’ll fail Amazon’s interview!”
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill Neglect&lt;/strong&gt;: Git? Databases? IDE shortcuts? “I’ll learn that later…”
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Echo Chambers&lt;/strong&gt;: Peers glorify “hard problem” counts, creating social pressure to conform.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This bandwagon effect mirrors Googhart’s Law perfectly. LeetCode was meant to &lt;strong&gt;assess problem-solving skills&lt;/strong&gt;, but students now treat it as the &lt;strong&gt;sole target&lt;/strong&gt; of their education. The result? A generation of developers who can reverse linked lists blindfolded but struggle to:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collaborate via version control (Git)
&lt;/li&gt;
&lt;li&gt;Design RESTful APIs
&lt;/li&gt;
&lt;li&gt;Optimize database queries
&lt;/li&gt;
&lt;li&gt;Configure CI/CD pipelines
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;The Hidden Costs of Algorithm-Only Obsession&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;The “Toy Problem” Trap&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;LeetCode problems are sanitized, self-contained challenges. Real-world codebases are messy, undocumented, and require:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debugging legacy systems
&lt;/li&gt;
&lt;li&gt;Writing maintainable code
&lt;/li&gt;
&lt;li&gt;Navigating monolithic architectures
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. &lt;strong&gt;Tooling Illiteracy&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Students memorize &lt;code&gt;for&lt;/code&gt; loops but don’t know:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to use &lt;code&gt;git rebase&lt;/code&gt; to clean commit histories
&lt;/li&gt;
&lt;li&gt;Why indexing matters in SQL databases
&lt;/li&gt;
&lt;li&gt;How to profile memory usage in IDEs
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. &lt;strong&gt;The Innovation Deficit&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Endless pattern-matching on coding platforms stifles creativity. Building projects (e.g., a custom API, a CLI tool) teaches &lt;strong&gt;systems thinking&lt;/strong&gt;—a skill no amount of two-pointer problems can replicate.  &lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;A Better Path: Rebalancing Your Learning&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. &lt;strong&gt;Apply the 70/30 Rule&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;70% Practical Skills&lt;/strong&gt;: Build projects using:

&lt;ul&gt;
&lt;li&gt;Version control (Git/GitHub)
&lt;/li&gt;
&lt;li&gt;Cloud platforms (AWS/Azure basics)
&lt;/li&gt;
&lt;li&gt;Databases (SQL + NoSQL)
&lt;/li&gt;
&lt;li&gt;APIs (REST/GraphQL)
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;30% Algorithms&lt;/strong&gt;: Solve LeetCode strategically—focus on patterns, not counts.
&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. &lt;strong&gt;Learn Tools, Not Just Syntax&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Debug a memory leak in VS Code
&lt;/li&gt;
&lt;li&gt;Optimize Docker container sizes
&lt;/li&gt;
&lt;li&gt;Write unit tests with Jest/JUnit
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. &lt;strong&gt;Embrace “Unsexy” Skills&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Documentation writing
&lt;/li&gt;
&lt;li&gt;System design fundamentals
&lt;/li&gt;
&lt;li&gt;Basic DevOps practices
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. &lt;strong&gt;Interview Smarter&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Companies like Microsoft and Spotify now include:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pair programming sessions
&lt;/li&gt;
&lt;li&gt;Take-home projects
&lt;/li&gt;
&lt;li&gt;Tooling/architecture discussions
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion: Measure What Matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Googhart’s Law reminds us that &lt;strong&gt;metrics are proxies, not goals&lt;/strong&gt;. Solving 1,000 LeetCode problems won’t make you a great engineer—but understanding how to:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collaborate via pull requests
&lt;/li&gt;
&lt;li&gt;Containerize applications
&lt;/li&gt;
&lt;li&gt;Design fault-tolerant systems
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…will. The next time you open LeetCode, ask yourself: &lt;em&gt;“Am I learning to solve problems, or just gaming a system?”&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;Break the bandwagon cycle. Your future self—and your first engineering team—will thank you.  &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Call to Action&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Git Guru Challenge&lt;/strong&gt;: Spend a weekend mastering Git (try &lt;a href="https://ohmygit.org/" rel="noopener noreferrer"&gt;Oh My Git!&lt;/a&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build a Micro-Project&lt;/strong&gt;: Create a CRUD API with authentication + documentation
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Join a Hackathon&lt;/strong&gt;: Prioritize teamwork and tooling over algorithmic complexity
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The road to becoming a &lt;em&gt;complete developer&lt;/em&gt; starts when you look beyond the leaderboard. 🚀  &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the Author&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
DBC is a software engineer and educator passionate about helping students bridge the gap between academic theory and industry practice. Follow for more takes on pragmatic learning strategies.&lt;/p&gt;

&lt;p&gt;#LeetCode #CodingInterviews #GoodhartsLaw  #CSStudents #CodeNewbie #SoftwareEngineering #RealWorldCoding #DeveloperSkills #TechEducation #TechCareers #CareerGrowth #ProgrammingTips #ProgrammingCommunity #BreakTheCycle #SoftwareDevelopment #TechBlog &lt;/p&gt;

</description>
      <category>leetcode</category>
      <category>codinginterviews</category>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Boosting Your Productivity: Essential IntelliJ IDEA Features and Plugins for Spring Boot Development</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Mon, 10 Feb 2025 07:19:21 +0000</pubDate>
      <link>https://dev.to/dbc2201/boosting-your-productivity-essential-intellij-idea-features-and-plugins-for-spring-boot-development-30k</link>
      <guid>https://dev.to/dbc2201/boosting-your-productivity-essential-intellij-idea-features-and-plugins-for-spring-boot-development-30k</guid>
      <description>&lt;p&gt;As a Java Spring Boot developer, having the right tools and knowing how to use them effectively can significantly impact your productivity. IntelliJ IDEA, developed by JetBrains, is widely considered one of the most powerful IDEs for Java development. This guide will help you leverage IntelliJ IDEA’s built-in features and essential plugins to streamline your Spring Boot workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Built-in Features for Spring Boot Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Spring Initializr Integration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Seamless project creation using Spring Initializr directly from IntelliJ IDEA&lt;/li&gt;
&lt;li&gt;Visual interface for managing dependencies&lt;/li&gt;
&lt;li&gt;Pre-configured setup for a smooth start&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Smart Code Completion
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Context-aware suggestions for Spring configurations&lt;/li&gt;
&lt;li&gt;Auto-completion for &lt;code&gt;application.properties&lt;/code&gt; and &lt;code&gt;.yaml&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;Intelligent autowiring suggestions&lt;/li&gt;
&lt;li&gt;Spring-specific annotation completions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3️⃣ Spring Boot Run Configuration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Auto-detection of Spring Boot configurations&lt;/li&gt;
&lt;li&gt;Easy management of environment variables&lt;/li&gt;
&lt;li&gt;Profile switching for different environments&lt;/li&gt;
&lt;li&gt;Live reload support via &lt;strong&gt;Spring Boot DevTools&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔌 Essential Plugins for Spring Boot Development
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Spring Boot Assistant
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Quick navigation to Spring Boot endpoints&lt;/li&gt;
&lt;li&gt;Configuration properties documentation&lt;/li&gt;
&lt;li&gt;Built-in support for Actuator endpoints&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Database Tools and SQL
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Integrated database console&lt;/li&gt;
&lt;li&gt;Schema visualization and entity relationship diagrams&lt;/li&gt;
&lt;li&gt;SQL code completion and query execution&lt;/li&gt;
&lt;li&gt;Database version control integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3️⃣ Lombok Plugin
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reduces boilerplate code using Lombok annotations&lt;/li&gt;
&lt;li&gt;Seamless code generation for getters, setters, constructors, and more&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔍 Navigation and Refactoring Like a Pro
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Code Navigation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Click&lt;/code&gt; / &lt;code&gt;Cmd + Click&lt;/code&gt;: Jump to method/class definitions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + N&lt;/code&gt; / &lt;code&gt;Cmd + O&lt;/code&gt;: Search for classes quickly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Shift + N&lt;/code&gt; / &lt;code&gt;Cmd + Shift + O&lt;/code&gt;: Find any file instantly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Alt + B&lt;/code&gt;: Navigate Spring Beans effortlessly&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Powerful Refactoring Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Safe Delete to avoid accidental removals&lt;/li&gt;
&lt;li&gt;Extract Method/Variable for cleaner code&lt;/li&gt;
&lt;li&gt;Rename across projects with confidence&lt;/li&gt;
&lt;li&gt;Move Classes and Packages easily&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛠️ Debugging Made Easy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Advanced Debugging Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Conditional breakpoints for precise debugging&lt;/li&gt;
&lt;li&gt;Stream debugging to analyze Java Streams&lt;/li&gt;
&lt;li&gt;Thread debugging for multi-threaded apps&lt;/li&gt;
&lt;li&gt;Memory view to inspect object allocations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Built-in HTTP Client
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Send and test RESTful API requests directly in IntelliJ IDEA&lt;/li&gt;
&lt;li&gt;Support for environment variables in API requests&lt;/li&gt;
&lt;li&gt;Request history tracking&lt;/li&gt;
&lt;li&gt;JSON/XML response handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔥 Spring Boot Specific Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Configuration Properties Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Auto-completion for &lt;code&gt;application.properties&lt;/code&gt; and &lt;code&gt;application.yml&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Inline documentation for property values&lt;/li&gt;
&lt;li&gt;Quick fixes for incorrect configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Endpoints Overview
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Visual representation of REST endpoints&lt;/li&gt;
&lt;li&gt;Request mapping overview for better API understanding&lt;/li&gt;
&lt;li&gt;Security configuration visualization&lt;/li&gt;
&lt;li&gt;Actuator endpoint inspection&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧪 Testing Support for Robust Applications
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Test Generation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Auto-generate test classes with templates&lt;/li&gt;
&lt;li&gt;Test coverage analysis&lt;/li&gt;
&lt;li&gt;One-click test execution and debugging&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Spring Test Support
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Built-in support for &lt;strong&gt;MockMvc&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Profile-specific test configurations&lt;/li&gt;
&lt;li&gt;Test context caching for faster test execution&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔄 Version Control Made Simple
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Git Integration
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Built-in Git operations (commit, push, pull, merge, etc.)&lt;/li&gt;
&lt;li&gt;Branch management with interactive rebasing&lt;/li&gt;
&lt;li&gt;Conflict resolution tools with side-by-side comparisons&lt;/li&gt;
&lt;li&gt;Change tracking with inline annotations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Local History for Safety
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Automatic local versioning (even without Git!)&lt;/li&gt;
&lt;li&gt;Recover lost changes&lt;/li&gt;
&lt;li&gt;Compare file versions easily&lt;/li&gt;
&lt;li&gt;View directory history&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚡ Boost Your Productivity with Shortcuts &amp;amp; Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Must-Know Shortcuts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Alt + Insert&lt;/code&gt; / &lt;code&gt;Cmd + N&lt;/code&gt;: Generate code (getters, setters, constructors, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Space&lt;/code&gt;: Basic code completion&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl + Shift + Space&lt;/code&gt;: Smart code completion&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Alt + Enter&lt;/code&gt;: Show intention actions for quick fixes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Shift + Shift&lt;/code&gt;: Search everywhere&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ Live Templates for Fast Coding
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;psvm&lt;/code&gt;: &lt;code&gt;public static void main(String[] args)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sout&lt;/code&gt;: &lt;code&gt;System.out.println()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt;: Generate the main method quickly&lt;/li&gt;
&lt;li&gt;Customize your own live templates for frequent code snippets&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🛢️ Advanced Database Tools &amp;amp; JPA Support
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Database View
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Connect to multiple databases inside IntelliJ IDEA&lt;/li&gt;
&lt;li&gt;Schema visualization for better understanding&lt;/li&gt;
&lt;li&gt;Query console for executing SQL queries&lt;/li&gt;
&lt;li&gt;Export/import data easily&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2️⃣ JPA &amp;amp; Hibernate Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Generate &lt;strong&gt;Entity Relationship Diagrams&lt;/strong&gt; (ERD) for JPA entities&lt;/li&gt;
&lt;li&gt;JPA console for quick queries&lt;/li&gt;
&lt;li&gt;Seamless database migrations&lt;/li&gt;
&lt;li&gt;Schema synchronization with JPA annotations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  📌 Conclusion
&lt;/h2&gt;

&lt;p&gt;IntelliJ IDEA is a game-changer for Spring Boot development. By mastering these features and plugins, you can significantly improve your workflow and efficiency. Here are some key takeaways:&lt;/p&gt;

&lt;p&gt;✅ Learn keyboard shortcuts to speed up development&lt;br&gt;&lt;br&gt;
✅ Customize your IDE settings for better usability&lt;br&gt;&lt;br&gt;
✅ Keep your plugins up to date&lt;br&gt;&lt;br&gt;
✅ Explore new features regularly&lt;br&gt;&lt;br&gt;
✅ Use IntelliJ IDEA’s built-in tools instead of external ones when possible  &lt;/p&gt;

&lt;p&gt;Investing time in learning these tools will lead to increased productivity and cleaner, more maintainable code. 🚀 Happy coding!&lt;/p&gt;

&lt;h2&gt;
  
  
  🔗 Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📖 &lt;a href="https://www.jetbrains.com/idea/documentation/" rel="noopener noreferrer"&gt;IntelliJ IDEA Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📘 &lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/html/" rel="noopener noreferrer"&gt;Spring Boot Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔌 &lt;a href="https://plugins.jetbrains.com/" rel="noopener noreferrer"&gt;JetBrains Plugin Repository&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>intellij</category>
      <category>java</category>
      <category>ide</category>
      <category>springboot</category>
    </item>
    <item>
      <title>Embracing Change: How AI Can Enhance Human Potential</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Sat, 08 Feb 2025 12:41:21 +0000</pubDate>
      <link>https://dev.to/dbc2201/embracing-change-how-ai-can-enhance-human-potential-529h</link>
      <guid>https://dev.to/dbc2201/embracing-change-how-ai-can-enhance-human-potential-529h</guid>
      <description>&lt;p&gt;In the early days of computing, our interface with technology was as rudimentary as punch cards—stiff, error-prone, and requiring a steep learning curve. At the time, many were understandably anxious. The fear was palpable: new technologies would eventually render human roles obsolete. But history has a different story to tell.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Look Back at Technological Transformation
&lt;/h2&gt;

&lt;p&gt;When punch cards were the norm, interacting with a computer meant feeding in rows of holes to dictate every operation. This method, though groundbreaking, was far from user-friendly. Yet, as technology advanced, so did our interfaces. The emergence of keyboards, monitors, and later, the internet, didn’t just replace punch cards—they revolutionized how we worked. What was once a niche task became an entire field of expertise, spawning roles we couldn’t have imagined before.&lt;/p&gt;

&lt;p&gt;This transformation wasn’t about the computer taking over human roles but about computers becoming tools that amplified human abilities. We moved from a paradigm of manual, error-prone data entry to one where computers could process vast amounts of data quickly and accurately, allowing us to focus on creativity, strategy, and innovation.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI: The Next Evolutionary Step
&lt;/h2&gt;

&lt;p&gt;Today, Artificial Intelligence is poised to be the catalyst for the next significant leap in our technological journey. Much like earlier innovations, AI is not here to replace us but to enhance our capabilities. Here’s why:&lt;/p&gt;

&lt;p&gt;Augmentation, Not Replacement: AI can handle repetitive and data-intensive tasks, liberating us to concentrate on higher-level thinking. This symbiosis can drive creativity and problem-solving in unprecedented ways.&lt;/p&gt;

&lt;p&gt;Creation of New Roles: Just as the personal computer revolution birthed new industries and job roles, AI is expected to open up exciting avenues—ranging from AI ethics and oversight to new kinds of creative and analytical professions.&lt;/p&gt;

&lt;p&gt;Enhanced Efficiency and Productivity: With AI managing routine tasks, human workers can focus on strategy, customer engagement, and other areas that require a personal touch—areas where machines simply cannot compete.&lt;/p&gt;

&lt;p&gt;Opportunities for Upskilling: As AI takes over mundane tasks, there is a growing need for skilled professionals who can develop, manage, and improve these systems. This shift encourages continuous learning and skill development, positioning workers to thrive in an AI-enhanced environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overcoming the Fear of Change
&lt;/h2&gt;

&lt;p&gt;It’s natural to feel apprehensive when facing rapid change. Yet, history reminds us that the interface through which we interact with technology is always evolving. Instead of resisting this inevitable progress, we should strive to harness it. By embracing AI as an assistant rather than a competitor, we unlock the potential for a future where human ingenuity and machine efficiency work in tandem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking to the Future
&lt;/h2&gt;

&lt;p&gt;The rise of AI is not a signal for the end of human employment but rather an invitation to innovate and explore new frontiers. By accepting AI as a permanent fixture in our technological landscape, we set the stage for exciting developments that can enhance our daily lives, improve business operations, and lead to societal advancements that benefit everyone.&lt;/p&gt;

&lt;p&gt;As we stand on the cusp of this new era, it’s crucial to remember that every technological revolution has faced similar skepticism. Just as we once embraced the transformation from punch cards to personal computers, today we must welcome AI—not as a harbinger of doom, but as a powerful ally in our quest to achieve more, think bigger, and live better.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>programming</category>
    </item>
    <item>
      <title>News of RPA cutting down about 10 million jobs in India</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Thu, 17 Jun 2021 10:26:17 +0000</pubDate>
      <link>https://dev.to/dbc2201/news-of-rpa-cutting-down-about-10-million-jobs-in-india-38c1</link>
      <guid>https://dev.to/dbc2201/news-of-rpa-cutting-down-about-10-million-jobs-in-india-38c1</guid>
      <description>&lt;p&gt;Recently, there has been a lot of commotion in the news where people are talking about "their worst fears coming to reality", "robots taking over the world", etc. The headlines being somewhere like robots forcing companies to cut down on about 10 million jobs, saving them about 7 lakh crore of money in revenues. People are freaking out over it as if their jobs will be gone now and they will be left helpless.&lt;/p&gt;

&lt;p&gt;Seems plausible, right? So I wanted to shed some light around the topic, since I'm currently studying RPA professionally as part of my job currently and soon will be teaching other professionals on how to get ready for RPA in their jobs. So, I'm not an "expert" on the subject, but being closely related to the IT industry in India for about 7+ years now, and now seeing RPA in action with my own eyes for some time, I thought I could help clear out the confusions people have about the subject and news.&lt;/p&gt;

&lt;p&gt;First of all, RPA (Robotic Process Automation) is basically creating some software (or code) called "robots", which can do some menial, monotonous process for an employee of a company. According to UiPath (a company working extensively with RPA, valued at 1 billion dollars) says this about RPA &lt;strong&gt;"RPA is the technology that allows computer software to mimic actions typically performed by humans interacting with digital systems to execute business processes."&lt;/strong&gt;. Scary, right? &lt;/p&gt;

&lt;p&gt;Well, not so much. RPA is currently not at a position where it could be applied to all the jobs of the IT industry in the worlds, or it could possibly take over complete aspects of a person's job. Currently, it is more suited to some specific areas like: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Payroll Processing&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Payroll processing refers to the actions that companies take to pay their employees - keeping track of their presence, of their salaries, bonuses and taxes. &lt;br&gt;
Payroll processing needs manual intervention month after month, every year. An RPA system can be used to extract the details that are required from handwritten time sheets and calculate the pay from their stipulated contracts and pay them as well (by even ordering the necessary bank transactions).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Client Information Updates&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Any organization that has implemented a CRM faces all sorts of related issues - the client-base is spread across many geographies, there are frequent calls to the back-end databases, and updates and changes are coming from all sources.&lt;br&gt;
RPA solutions can process these requests in batches instead of one after the other, reducing the load on the back-end systems and ensuring better performance and data quality across the whole application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Renewal Process&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Irrespective of industry, the contract renewal process is in general a complex process, but not necessarily due to exceptions and complications, but rather to the number of operations and the synchronization between different departments and systems.&lt;br&gt;
Robots can take over the entire process, starting with the standardized communication with the client, processing the changes, drafting the documents and updating the internal systems accordingly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Financial Statement Reconciliation&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Financial statement reconciliation covers all the operations (done mostly by the accounting teams) of matching orders, payments, losses, margins, and so on, with accounts and financial statements. It is a common process that an organization needs to manage in order to ensure clean records and reliable financial documents.&lt;br&gt;
This process is well handled by the RPA software robots. Once they are set up, they can seamlessly replace the humans who would have to do these jobs, from the beginning to the end.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compliance Reporting&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
As organizations grow, it becomes increasingly difficult to closely monitor the compliance requirements that each department has to follow: reporting to authorities, complying with the internal procedures, audit requirements, and so on.&lt;br&gt;
Robots can be set up to cover all these needs, with a low error rate and low human intervention.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customer Complaint Processing&lt;/strong&gt;:&lt;br&gt;&lt;br&gt;
Irrespective of industry, customer complaints are always on the radar. Their number and substance is an important indicator of the business health and good predictor of the future of the company.&lt;br&gt;
Through RPA, customer complaints can be categorized based on keywords and other criteria, and possible solutions can be suggested to the customers right away. By doing so, the customer complaints can be answered 24 x 7 instead of 8 hours a day and only 5 days a week.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If I were to define RPA, I would RPA is a way of easing up the repetitive parts of a person's job, by creating specialized robots to do it. But it is a long way from actually taking over the job itself. So, people do not need to worry a lot, even if it looks like there are things that you do in the above list, and it could be taken over by a robot, it is not going to happen soon, so you will get some time to potentially upgrade your skills to get a better job.&lt;/p&gt;

&lt;p&gt;Hope I was able to help you put your mind at ease, if you would like to know more, please watch these videos&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/dJ9c2xq6mas"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/davZONRujxE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/pcRrKFsjqtU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/3R67RGjZoOM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>rpa</category>
      <category>jobs</category>
      <category>india</category>
      <category>news</category>
    </item>
    <item>
      <title>A Simple Roadmap for Java Beginners in 2021</title>
      <dc:creator>dbc2201</dc:creator>
      <pubDate>Thu, 13 May 2021 14:06:00 +0000</pubDate>
      <link>https://dev.to/dbc2201/a-simple-roadmap-for-java-beginners-in-2021-3k06</link>
      <guid>https://dev.to/dbc2201/a-simple-roadmap-for-java-beginners-in-2021-3k06</guid>
      <description>&lt;h2&gt;
  
  
  This post is for you if you can answer "yes" to all/most of the questions below:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;You are an engineering undergrad in a four-year course.&lt;/li&gt;
&lt;li&gt;Your major is computer science &amp;amp; engineering.&lt;/li&gt;
&lt;li&gt;You are inclined towards programming.&lt;/li&gt;
&lt;li&gt;You are thinking about a career in programming.&lt;/li&gt;
&lt;li&gt;You have heard about Java from your friends/peers and want to learn it.&lt;/li&gt;
&lt;li&gt;You have some form of introduction to the Java programming language but you want to further increase your knowledge.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is easy to get overwhelmed these days by looking at what other people are doing in the programming field. Considering the variety of low-code/no-code options available out there to "create" software. Being a teacher for about 6-7 years, I've had my fair share of students coming up to me and asking "What should we learn?". &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa1ewugkp6nw4x4r2owp1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa1ewugkp6nw4x4r2owp1.jpg" alt="Photo by Edmond Dantès from Pexels" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To which, I mostly answer "Well, start with a programming language, and learn it well." Little did I know in the beginning, that this statement mostly does more harm than good to a student who is confused about their career paths, or what choice do they make given the opportunities that they have currently been exposed to.&lt;/p&gt;

&lt;p&gt;They (students) then start asking me a plethora of questions that are largely subjective and depend on the student itself. Sometimes, it is also a matter of taking initiative, and/or deciding on a choice and seeing it through, which I have observed is a lacking ingredient in the younger generation these days. The follow-up questions that I then receive are somewhat like this: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which is the "best" programming language?&lt;/li&gt;
&lt;li&gt;Is the "X" programming language better than the "Y" programming language? Why?&lt;/li&gt;
&lt;li&gt;Where should I start from?&lt;/li&gt;
&lt;li&gt;What all do I need to learn to land a job?&lt;/li&gt;
&lt;li&gt;I watched this YouTube video of this generic YouTuber person, will this suffice?&lt;/li&gt;
&lt;li&gt;Do I need to purchase an expensive course for this?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkpash3hb5w3g3x95vxu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkpash3hb5w3g3x95vxu.gif" alt="Alt Text" width="680" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is completely okay if you have had the same question in your mind for some time, I had the same questions myself when I started programming seriously.&lt;/p&gt;

&lt;p&gt;As I have experienced, if you're willing to learn Java (since this post was originally about Java) in 2021, your journey from a "beginner" to a "not-a-beginner-anymore" stage will consist of largely 4 stages.&lt;/p&gt;

&lt;p&gt;Before we begin exploring things, I would just like to make a disclaimer that a lot of people will tell you otherwise, they'll often come up to you and say "Oh, it's so easy, just do "this", watch this "video", enrol in this course..." which is complete bollocks. Learning a programming language is exactly like learning a new spoken language, your brain needs to remove or at least ignore your previous bias and get used to thinking in terms of the new language. For that, you need to put in a constant effort and hard work. If it were actually that easy as some people say, everyone you know would have been a rockstar developer and we would have been living in a programming utopia, free of problems, (maybe riddled with bugs, lol).&lt;/p&gt;

&lt;p&gt;Just remember, what &lt;strong&gt;EVERY&lt;/strong&gt; programming language does is tell a computer what to do!. In the end, everything gets translated for 1s and 0s for your CPU so it does not matter what language you use to "code" your next big app or website! Languages are for the efficiency and benefit of developers, not the computers.&lt;/p&gt;

&lt;p&gt;To start I would recommend this book : &lt;a href="https://www.mheducation.co.in/java-a-beginners-guide-9789390385287-india" rel="noopener noreferrer"&gt;Java: A Beginner's Guide by Herbert Schildt&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Syntax and Semantics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Data Types (The 8 Java Primitive Types)&lt;/li&gt;
&lt;li&gt;Variables (The &lt;code&gt;var&lt;/code&gt; keyword)&lt;/li&gt;
&lt;li&gt;Type Conversion &amp;amp; Casting&lt;/li&gt;
&lt;li&gt;Automatic Type Promotion&lt;/li&gt;
&lt;li&gt;Arrays&lt;/li&gt;
&lt;li&gt;Operators in Java (Arithmetic, Bitwise, Relational, Boolean Logical, Assignment, &lt;code&gt;?&lt;/code&gt; Operator, Operator Precedence)&lt;/li&gt;
&lt;li&gt;Control Statements in Java (If, If-Else, If-Else-If, Switch (new and old))&lt;/li&gt;
&lt;li&gt;Iteration Statements (While, Do-While, For, For-Each, Nested Loops) [continue, break and labels]&lt;/li&gt;
&lt;li&gt;Using Command-Line Arguments in a Java Program&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;String&lt;/code&gt; class&lt;/li&gt;
&lt;li&gt;Varargs [Variable Length Arguments]&lt;/li&gt;
&lt;li&gt;Scanner Class, BufferedReader Class&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Object-Oriented Programming:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Types of Program Units in Java [4 - Class, Abstract Class, Interface, Enum + 1 Record] &lt;/li&gt;
&lt;li&gt;Declaring and Using Objects in Methods&lt;/li&gt;
&lt;li&gt;Reference Types and Values&lt;/li&gt;
&lt;li&gt;Methods (method signature, method types)&lt;/li&gt;
&lt;li&gt;Constructors&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;this&lt;/code&gt; keyword&lt;/li&gt;
&lt;li&gt;Overloading Methods&lt;/li&gt;
&lt;li&gt;Overloading Constructors&lt;/li&gt;
&lt;li&gt;Using Objects (Reference Types) as method parameters and arguments&lt;/li&gt;
&lt;li&gt;Using Objects (Reference Types) as return types of methods&lt;/li&gt;
&lt;li&gt;Recursion in Java (Call Stack, Tail-Recursion, Head-Recursion)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;static&lt;/code&gt; keyword (creating &lt;code&gt;static&lt;/code&gt; members in a class)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;final&lt;/code&gt; keyword (with a class, with a method, with a member variable of a class)&lt;/li&gt;
&lt;li&gt;Nested Classes and Inner Classes&lt;/li&gt;
&lt;li&gt;Inheritance in Java (Super &amp;amp; subclasses) [Hierarchies in programs]&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;super&lt;/code&gt; keyword (calling Super class' members from the subclass)&lt;/li&gt;
&lt;li&gt;Types of Hierarchies in Java [Simple, Multi-level, Multiple, Hybrid]&lt;/li&gt;
&lt;li&gt;Method Overriding&lt;/li&gt;
&lt;li&gt;Abstract Classes and Abstract Members&lt;/li&gt;
&lt;li&gt;Interfacing (defining an interface, implementing an interface, nested interfaces, members in an interface, private OR static OR default methods)&lt;/li&gt;
&lt;li&gt;Packages in a Java program (CLASSPATH, importing)&lt;/li&gt;
&lt;li&gt;Exception Handling (exception types, caught and uncaught exceptions, try-catch, try-finally, try-catch-finally, multiple catch blocks, nested try statements, &lt;code&gt;throw&lt;/code&gt; and &lt;code&gt;throws&lt;/code&gt; keyword, custom exceptions)&lt;/li&gt;
&lt;li&gt;Multithreaded Programming (Java Thread Model, Main Thread, Creating a Thread, &lt;code&gt;Thread&lt;/code&gt; class and &lt;code&gt;Runnable&lt;/code&gt; interface, multiple threads, &lt;code&gt;sleep()&lt;/code&gt; method, thread priorities, using lambda to create a thread, using anonymous class to create a thread)&lt;/li&gt;
&lt;li&gt;Enumeration Classes in Java&lt;/li&gt;
&lt;li&gt;Type Wrappers (Wrapper Classes in Java)&lt;/li&gt;
&lt;li&gt;Boxing and Unboxing&lt;/li&gt;
&lt;li&gt;Annotations in Java&lt;/li&gt;
&lt;li&gt;Generics in Java (Bounded Types, Wildcard Types, Bounded Wildcards, generic methods, generic constructors, generics with static methods, generic classes, generic interfaces, overloading and overriding in generic methods, Type Erasure, generic arrays)&lt;/li&gt;
&lt;li&gt;Lambda Expression (functional interfaces, lambda as arguments, method references)&lt;/li&gt;
&lt;li&gt;Stream API (types of methods, common methods from the API)&lt;/li&gt;
&lt;li&gt;String Handling (String class constructors, string length, string operations, character extraction, string comparison, searching strings, modifying strings, StringBuilder, StringBuffer)&lt;/li&gt;
&lt;li&gt;Classes in the &lt;code&gt;java.lang&lt;/code&gt; package&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Data Structures &amp;amp; Algorithms:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Collections Framework in Java (collection interfaces - List, Set, Deque, collection classes, - ArrayList, LinkedList, HashSet, TreeSet, PriorityQueue, ArrayDeque, using iterators on collections, Iterator class, for-each loop, streams, Custom Collections Classes, Map Interfaces and Map Classes, Vector, Stack, Dictionary, Hashtable, Properties)&lt;/li&gt;
&lt;li&gt;Misc - StringTokenizer, BitSet, Optional, LocalDate, LocalTime, LocalDateTime, GregorianCalendar, TimeZone, Locale, Random, Timer, Currency, NumberFormat, StringFormat.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Problem Solving:
&lt;/h3&gt;

&lt;p&gt;When you have some decent knowledge of the topics written above, you can try your hands on some problem solving (a.k.a Competitive Programming, CP). Some recommended websites are &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.topcoder.com/" rel="noopener noreferrer"&gt;TopCoder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codeforces.com/" rel="noopener noreferrer"&gt;CodeForces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.coderbyte.com/" rel="noopener noreferrer"&gt;CoderByte&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://projecteuler.net/" rel="noopener noreferrer"&gt;Project Euler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hackerrank.com/dashboard" rel="noopener noreferrer"&gt;HackerRank&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.codechef.com/" rel="noopener noreferrer"&gt;CodeChef&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://exercism.io/" rel="noopener noreferrer"&gt;Exercism&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.codewars.com/" rel="noopener noreferrer"&gt;CodeWars&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://leetcode.com/" rel="noopener noreferrer"&gt;LeetCode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.spoj.com/" rel="noopener noreferrer"&gt;Sphere Online Judge SPOJ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.codingame.com/start" rel="noopener noreferrer"&gt;CodingGame&lt;/a&gt;
Choose whichever one you like all of them are for the same thing, to help you build up your knowledge of the language.
There are some nice books as well which help you learn the basics of Competitive Programming like &lt;/li&gt;
&lt;li&gt;&lt;a href="https://link.springer.com/book/10.1007/978-3-319-72547-5" rel="noopener noreferrer"&gt;Guide to Competitive Programming
Learning and Improving Algorithms Through Contests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.packtpub.com/product/java-coding-problems/9781789801415" rel="noopener noreferrer"&gt;Java Coding Problems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.oreilly.com/library/view/the-complete-coding/9781839212062/" rel="noopener noreferrer"&gt;The The Complete Coding Interview Guide in Java&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The intentional order of the sections is 1-2-3-4, although you could do section 4 as per your comfort, you can very well start section 4 after section 2 as well. &lt;strong&gt;PLEASE NOTE&lt;/strong&gt; that this is not the complete list, there is still a lot more to cover, this is just to get you comfortable enough that you could try anything with Java after this. Hope this blog post will help at least some of you! &lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>beginners</category>
      <category>roadmap</category>
    </item>
  </channel>
</rss>
