<?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: nocklock</title>
    <description>The latest articles on DEV Community by nocklock (@nocklock).</description>
    <link>https://dev.to/nocklock</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4134077%2Fc0409955-6d5a-4cd1-9812-e9e8d412bb41.png</url>
      <title>DEV Community: nocklock</title>
      <link>https://dev.to/nocklock</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nocklock"/>
    <language>en</language>
    <item>
      <title>Git on a New Windows PC: init vs clone, origin, -M, -u, and Your First Push</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Sun, 04 Oct 2026 23:12:05 +0000</pubDate>
      <link>https://dev.to/nocklock/git-on-a-new-windows-pc-init-vs-clone-origin-m-u-and-your-first-push-4lkp</link>
      <guid>https://dev.to/nocklock/git-on-a-new-windows-pc-init-vs-clone-origin-m-u-and-your-first-push-4lkp</guid>
      <description>&lt;p&gt;I recently set up Git again on a new Windows laptop.&lt;/p&gt;

&lt;p&gt;Installing Git itself wasn't difficult.&lt;/p&gt;

&lt;p&gt;What was more interesting was seeing commands I had used many times before as if I were looking at them for the first time again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config
git init
git remote
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I knew how to use them.&lt;/p&gt;

&lt;p&gt;But while setting up a new machine, I caught myself asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why am I setting &lt;code&gt;user.name&lt;/code&gt; and &lt;code&gt;user.email&lt;/code&gt; again?&lt;/p&gt;

&lt;p&gt;What's the actual difference between &lt;code&gt;git init&lt;/code&gt; and &lt;code&gt;git clone&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;What exactly is &lt;code&gt;origin&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;And what do &lt;code&gt;-M&lt;/code&gt; and &lt;code&gt;-u&lt;/code&gt; actually mean?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So instead of just copying the commands and moving on, I went through the flow again.&lt;/p&gt;

&lt;p&gt;This is the version I wish I had when I first started using Git.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. First, make sure Git is actually installed
&lt;/h2&gt;

&lt;p&gt;After installing Git on Windows, I start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Git is available from the terminal, you should see the installed version.&lt;/p&gt;

&lt;p&gt;Simple, but I like checking the command itself instead of assuming the installer finished everything correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Configure your Git identity
&lt;/h2&gt;

&lt;p&gt;On a new machine, Git does not automatically know who should be recorded as the author of your commits.&lt;/p&gt;

&lt;p&gt;Set your name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"Your Name"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And your email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check them again with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name
git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or inspect the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing that confused me when I first learned Git was thinking this was basically my GitHub login.&lt;/p&gt;

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

&lt;p&gt;These values are used as author information for commits.&lt;/p&gt;

&lt;p&gt;If Git does not know your identity when it needs to create a commit, you may run into messages like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Please tell me who you are.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fatal: unable to auto-detect email address
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So on a new development machine, this is one of the first Git settings I check.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. &lt;code&gt;git init&lt;/code&gt; vs &lt;code&gt;git clone&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;These two commands are easy to mix together at first because both can be part of "starting a Git project."&lt;/p&gt;

&lt;p&gt;But they start from different situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  You already have a project on your computer
&lt;/h3&gt;

&lt;p&gt;If you have a local folder and want Git to start tracking it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This initializes a Git repository inside the current directory.&lt;/p&gt;

&lt;p&gt;I think of it as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I already have this folder.
Now I want Git to manage it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The repository already exists remotely
&lt;/h3&gt;

&lt;p&gt;If the project is already on GitHub or another Git server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &amp;lt;repository-url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/example/project.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This downloads the existing repository and its Git history.&lt;/p&gt;

&lt;p&gt;So the simple version is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Existing local project
→ git init

Existing remote repository
→ git clone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You normally do not need to run &lt;code&gt;git init&lt;/code&gt; before cloning an existing repository.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Add your files and create the first commit
&lt;/h2&gt;

&lt;p&gt;After initializing a local repository, I usually check its current state first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then stage the files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And create a commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the commit exists in the local Git repository.&lt;/p&gt;

&lt;p&gt;It is not on GitHub yet.&lt;/p&gt;

&lt;p&gt;That distinction is important.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit
→ records the change locally

git push
→ sends commits to a remote repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. What does &lt;code&gt;origin&lt;/code&gt; actually mean?
&lt;/h2&gt;

&lt;p&gt;After creating a repository on GitHub, you can connect the local repository to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin &amp;lt;repository-url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin https://github.com/example/project.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I first used Git, &lt;code&gt;origin&lt;/code&gt; felt like some special Git keyword.&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;origin&lt;/code&gt; is simply the conventional name Git uses for a remote repository.&lt;/p&gt;

&lt;p&gt;You could technically give the remote another name.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;origin&lt;/code&gt; is the standard name you will see almost everywhere.&lt;/p&gt;

&lt;p&gt;You can check the configured remotes with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when you later run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can roughly read it as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;push
my main branch
to the remote repository named origin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That mental model made the command much easier to remember.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. What does &lt;code&gt;git branch -M main&lt;/code&gt; mean?
&lt;/h2&gt;

&lt;p&gt;GitHub often shows this command when connecting a new local repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important parts are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main
→ the branch name

-M
→ rename the branch, forcing the rename if necessary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this command renames the current branch to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The uppercase &lt;code&gt;-M&lt;/code&gt; is effectively the force version of the branch rename option.&lt;/p&gt;

&lt;p&gt;For most new repositories, the practical thing to remember is simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Make the current branch name main.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. What does &lt;code&gt;-u&lt;/code&gt; mean in &lt;code&gt;git push -u origin main&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Now comes the command I had typed many times without really thinking about every part of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We already know:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;origin
→ the remote repository

main
→ the branch being pushed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So what does &lt;code&gt;-u&lt;/code&gt; do?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-u&lt;/code&gt; is shorthand for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--set-upstream
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It connects your local branch with the corresponding remote branch as its upstream tracking branch.&lt;/p&gt;

&lt;p&gt;After that first push, you can usually use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And similarly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;can use the configured upstream relationship.&lt;/p&gt;

&lt;p&gt;So I now think of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Push main to origin,
and remember that relationship for later.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is much easier to remember than treating &lt;code&gt;-u&lt;/code&gt; as a mysterious flag.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. The full flow from a local project to GitHub
&lt;/h2&gt;

&lt;p&gt;For a new local project, the basic flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init

git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"Your Name"&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;

git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial commit"&lt;/span&gt;

git remote add origin &amp;lt;repository-url&amp;gt;

git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main

git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the upstream relationship is configured, the normal daily flow becomes much shorter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Describe the change"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the part I actually use all the time.&lt;/p&gt;




&lt;h2&gt;
  
  
  What changed for me after setting Git up again
&lt;/h2&gt;

&lt;p&gt;None of these commands were new to me.&lt;/p&gt;

&lt;p&gt;I had used Git for years.&lt;/p&gt;

&lt;p&gt;But a development machine that has been configured for a long time hides a lot of setup.&lt;/p&gt;

&lt;p&gt;On my old machine, Git already knew my identity.&lt;/p&gt;

&lt;p&gt;The repositories already had remotes.&lt;/p&gt;

&lt;p&gt;The branches already tracked their upstream branches.&lt;/p&gt;

&lt;p&gt;I could just open a project and start working.&lt;/p&gt;

&lt;p&gt;A new laptop removes all of those assumptions.&lt;/p&gt;

&lt;p&gt;And once I had to configure them again, commands like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git config
git init
origin
-M
-u
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;stopped looking like one long setup recipe.&lt;/p&gt;

&lt;p&gt;I could see what each step was actually doing.&lt;/p&gt;

&lt;p&gt;I still don't think every Git option needs to be memorized.&lt;/p&gt;

&lt;p&gt;But knowing whether a command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;creating a local repository
recording a commit
connecting a remote
renaming a branch
or pushing code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;makes Git much less confusing when something goes wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  One thing I would not do
&lt;/h2&gt;

&lt;p&gt;When a Git command fails, I try not to immediately paste a force command from the first search result.&lt;/p&gt;

&lt;p&gt;Commands such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or destructive reset options can change history or discard work.&lt;/p&gt;

&lt;p&gt;Before using them, I want to understand why Git is refusing the operation.&lt;/p&gt;

&lt;p&gt;That is especially true once other people are working on the same repository.&lt;/p&gt;




&lt;h2&gt;
  
  
  After the first push
&lt;/h2&gt;

&lt;p&gt;Getting the repository onto GitHub is only the beginning.&lt;/p&gt;

&lt;p&gt;The next confusing Git questions tend to be things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is the difference between &lt;code&gt;git fetch&lt;/code&gt; and &lt;code&gt;git pull&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Why was my push rejected as &lt;code&gt;non-fast-forward&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What does &lt;code&gt;detached HEAD&lt;/code&gt; actually mean?&lt;/li&gt;
&lt;li&gt;When should I use &lt;code&gt;revert&lt;/code&gt;, &lt;code&gt;reset&lt;/code&gt;, or &lt;code&gt;restore&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those deserve their own explanations rather than being squeezed into a first-push guide.&lt;/p&gt;




&lt;h2&gt;
  
  
  Shipping the project after Git is ready
&lt;/h2&gt;

&lt;p&gt;Getting your code into Git is one step.&lt;/p&gt;

&lt;p&gt;Shipping a Spring Boot project is another.&lt;/p&gt;

&lt;p&gt;I put the basic version of my release workflow here:&lt;/p&gt;

&lt;h3&gt;
  
  
  Ship Your Spring Boot MVP — Free Edition
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/nocklock-h/ship-your-spring-boot-mvp-free" rel="noopener noreferrer"&gt;Get the free Spring Boot MVP checklist on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want the complete checklist and release workflow:&lt;/p&gt;

&lt;h3&gt;
  
  
  Ship Your Spring Boot MVP — Full Edition
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nocklock.lemonsqueezy.com/checkout/buy/587f273f-2eb7-4e55-9ad7-cfdcba6acc99" rel="noopener noreferrer"&gt;Get the Full Edition&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If I had to keep only one mental model from this setup, it would be this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;init&lt;/code&gt; starts local Git.&lt;br&gt;&lt;br&gt;
&lt;code&gt;commit&lt;/code&gt; records locally.&lt;br&gt;&lt;br&gt;
&lt;code&gt;remote&lt;/code&gt; connects somewhere else.&lt;br&gt;&lt;br&gt;
&lt;code&gt;push&lt;/code&gt; sends commits there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flags are much easier to remember once the flow itself makes sense.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>IntelliJ Java Version Mismatch: Project SDK, Gradle JVM, and JAVA_HOME Explained</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Sun, 04 Oct 2026 23:07:51 +0000</pubDate>
      <link>https://dev.to/nocklock/intellij-java-version-mismatch-project-sdk-gradle-jvm-and-javahome-explained-1oai</link>
      <guid>https://dev.to/nocklock/intellij-java-version-mismatch-project-sdk-gradle-jvm-and-javahome-explained-1oai</guid>
      <description>&lt;h1&gt;
  
  
  IntelliJ Java Version Mismatch: Project SDK, Gradle JVM, and JAVA_HOME Explained
&lt;/h1&gt;

&lt;p&gt;I recently set up a new development laptop from scratch.&lt;/p&gt;

&lt;p&gt;JDK 17 was installed.&lt;/p&gt;

&lt;p&gt;The terminal looked fine.&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;-version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returned the version I expected.&lt;/p&gt;

&lt;p&gt;That should mean my Java environment is ready, right?&lt;/p&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;While setting up IntelliJ again, I was reminded of something that is easy to forget once a development machine has been configured for years:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Java version shown in your terminal is not always the Java version IntelliJ or Gradle is actually using.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are several places where Java versions can be configured, and they do not automatically mean the same thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Start with the Java version your terminal is using
&lt;/h2&gt;

&lt;p&gt;The first thing I check is still the simplest one.&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;-version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also check the compiler version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;javac &lt;span class="nt"&gt;-version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Windows, you can also inspect &lt;code&gt;JAVA_HOME&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Prompt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;%JAVA_HOME%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  PowerShell
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;JAVA_HOME&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if multiple JDKs have been installed before, it is useful to check which Java executable is actually being resolved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Prompt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="nb"&gt;where&lt;/span&gt; &lt;span class="kd"&gt;java&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  PowerShell
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-Command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;java&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells you what your terminal sees.&lt;/p&gt;

&lt;p&gt;But this is only the first layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Check IntelliJ Project SDK
&lt;/h2&gt;

&lt;p&gt;In IntelliJ IDEA, check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;File
→ Project Structure
→ Project
→ SDK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the JDK configured for the project inside IntelliJ.&lt;/p&gt;

&lt;p&gt;A common assumption is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;java -version = 17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;therefore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IntelliJ Project SDK = 17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But those are separate settings.&lt;/p&gt;

&lt;p&gt;Your system may use one JDK while IntelliJ points to another.&lt;/p&gt;

&lt;p&gt;That is why checking only the terminal can be misleading.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Do not forget the Gradle JVM
&lt;/h2&gt;

&lt;p&gt;For Gradle projects, there is another setting that is easy to overlook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings
→ Build, Execution, Deployment
→ Build Tools
→ Gradle
→ Gradle JVM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important distinction is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Project SDK
→ JDK configured for the IntelliJ project

Gradle JVM
→ JVM used to run Gradle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this configuration is completely possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System Java: 17
Project SDK: 17
Gradle JVM: 11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, everything looks like a Java 17 project.&lt;/p&gt;

&lt;p&gt;Then the Gradle build starts behaving differently.&lt;/p&gt;

&lt;p&gt;That is usually the point where the setup becomes confusing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Project SDK and Gradle JVM are related, but they are not the same setting.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Check what Gradle is actually running with
&lt;/h2&gt;

&lt;p&gt;If the project uses the Gradle Wrapper, you can check it directly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Prompt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="kd"&gt;gradlew&lt;/span&gt;.bat &lt;span class="na"&gt;--version
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  PowerShell
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\gradlew.bat&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output includes the JVM Gradle is running with.&lt;/p&gt;

&lt;p&gt;This is useful when IntelliJ looks correct but the build is still complaining about Java versions.&lt;/p&gt;

&lt;p&gt;Instead of changing random settings, you can verify which JVM each layer is actually using.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Your project may define its own Java requirement
&lt;/h2&gt;

&lt;p&gt;The project itself can also specify the Java version.&lt;/p&gt;

&lt;p&gt;For example, using a Gradle Java Toolchain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;toolchain&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;languageVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JavaLanguageVersion&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="mi"&gt;17&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;Older or existing projects may also contain something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;sourceCompatibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'17'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The exact configuration varies between projects.&lt;/p&gt;

&lt;p&gt;The important point is that your machine configuration is not the only thing that matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The project may have its own Java requirement too.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Java version problems become confusing
&lt;/h2&gt;

&lt;p&gt;The problem is not simply "Java 17 vs Java 21."&lt;/p&gt;

&lt;p&gt;The confusing part is that Java can appear in several places:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Windows Java
     ↓
JAVA_HOME / PATH

IntelliJ
     ↓
Project SDK

Gradle
     ↓
Gradle JVM

Project
     ↓
build.gradle / toolchain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These settings are related.&lt;/p&gt;

&lt;p&gt;But they are not identical.&lt;/p&gt;

&lt;p&gt;That is the part I had almost stopped thinking about because my previous development machine had already been configured for years.&lt;/p&gt;

&lt;p&gt;On a new laptop, all of those invisible assumptions become visible again.&lt;/p&gt;




&lt;h2&gt;
  
  
  When I see a Java version error, this is what I check first
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;First things to check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Terminal and IntelliJ show different Java versions&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;JAVA_HOME&lt;/code&gt;, &lt;code&gt;PATH&lt;/code&gt;, Project SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gradle build complains about Java version&lt;/td&gt;
&lt;td&gt;Gradle JVM, &lt;code&gt;gradlew --version&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;invalid source release&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Project SDK, Gradle JVM, project Java configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A dependency requires a newer JVM&lt;/td&gt;
&lt;td&gt;Gradle JVM and runtime JVM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project worked on the old machine but not the new one&lt;/td&gt;
&lt;td&gt;JDK, environment variables, Gradle JVM, project configuration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The exact error may be different.&lt;/p&gt;

&lt;p&gt;But checking &lt;strong&gt;which layer is actually using which Java version&lt;/strong&gt; is usually more useful than immediately copying the first fix from a search result.&lt;/p&gt;




&lt;h2&gt;
  
  
  The setup order I use on a new machine
&lt;/h2&gt;

&lt;p&gt;For my new laptop, I kept it simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Install JDK
↓
Check java -version
↓
Install IntelliJ
↓
Check Project SDK
↓
Check Gradle JVM
↓
Run an existing project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last step matters the most.&lt;/p&gt;

&lt;p&gt;A development environment is not really finished just because the IDE opens.&lt;/p&gt;

&lt;p&gt;For me, it is finished when an existing project can build and run again.&lt;/p&gt;

&lt;p&gt;Setting up a new machine reminded me that many things we think of as "automatic" are really just configuration that was done a long time ago.&lt;/p&gt;

&lt;p&gt;Once you rebuild everything from zero, you start seeing those dependencies again.&lt;/p&gt;

&lt;p&gt;And Java version configuration is a good example.&lt;/p&gt;




&lt;h2&gt;
  
  
  Shipping the project after the environment works
&lt;/h2&gt;

&lt;p&gt;Getting the environment running is only the first step.&lt;/p&gt;

&lt;p&gt;Actually shipping a Spring Boot project comes with a different set of checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;configuration&lt;/li&gt;
&lt;li&gt;deployment&lt;/li&gt;
&lt;li&gt;verification&lt;/li&gt;
&lt;li&gt;release decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I put the basic version of that workflow here:&lt;/p&gt;

&lt;h3&gt;
  
  
  Ship Your Spring Boot MVP — Free Edition
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/nocklock-h/ship-your-spring-boot-mvp-free" rel="noopener noreferrer"&gt;Get the free Spring Boot MVP checklist on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want the full checklist and release workflow:&lt;/p&gt;

&lt;h3&gt;
  
  
  Ship Your Spring Boot MVP — Full Edition
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://nocklock.lemonsqueezy.com/checkout/buy/587f273f-2eb7-4e55-9ad7-cfdcba6acc99" rel="noopener noreferrer"&gt;Get the Full Edition&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you're setting up Java on a new machine, the main thing I would remember is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don't ask only, "Which Java version did I install?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ask, &lt;strong&gt;"Which Java version is each part of my development environment actually using?"&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question usually makes the problem much easier to trace.&lt;/p&gt;

</description>
      <category>java</category>
      <category>intellij</category>
      <category>gradle</category>
      <category>springboot</category>
    </item>
    <item>
      <title>AI Wasn't the Beginning. We Had Already Started Outsourcing Parts of Ourselves to Smartphones</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Sat, 03 Oct 2026 05:49:53 +0000</pubDate>
      <link>https://dev.to/nocklock/ai-wasnt-the-beginning-we-had-already-started-outsourcing-parts-of-ourselves-to-smartphones-3fnc</link>
      <guid>https://dev.to/nocklock/ai-wasnt-the-beginning-we-had-already-started-outsourcing-parts-of-ourselves-to-smartphones-3fnc</guid>
      <description>&lt;p&gt;People worry that we're becoming too dependent on AI.&lt;/p&gt;

&lt;p&gt;It writes for us.&lt;/p&gt;

&lt;p&gt;Searches for us.&lt;/p&gt;

&lt;p&gt;Helps us code.&lt;/p&gt;

&lt;p&gt;And increasingly, we ask it what we should do next.&lt;/p&gt;

&lt;p&gt;I understand the concern.&lt;/p&gt;

&lt;p&gt;I think about it too.&lt;/p&gt;

&lt;p&gt;But recently I started wondering:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did this really begin with AI?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The more I thought about it, the less it seemed that way.&lt;/p&gt;

&lt;p&gt;We've been outsourcing small parts of ourselves to technology for a long time.&lt;/p&gt;

&lt;p&gt;AI is just making it much easier to notice.&lt;/p&gt;




&lt;h2&gt;
  
  
  I used to memorize phone numbers
&lt;/h2&gt;

&lt;p&gt;When I was younger, I knew several phone numbers by memory.&lt;/p&gt;

&lt;p&gt;Family.&lt;/p&gt;

&lt;p&gt;Friends.&lt;/p&gt;

&lt;p&gt;Sometimes even friends' home numbers.&lt;/p&gt;

&lt;p&gt;You needed to know the number if you wanted to call someone.&lt;/p&gt;

&lt;p&gt;I barely do that anymore.&lt;/p&gt;

&lt;p&gt;I search for a name and tap it.&lt;/p&gt;

&lt;p&gt;The phone remembers for me.&lt;/p&gt;

&lt;p&gt;And honestly, this doesn't feel like a loss most of the time.&lt;/p&gt;

&lt;p&gt;It's convenient.&lt;/p&gt;

&lt;p&gt;There is very little reason for me to store dozens of phone numbers in my head when a device can do it better.&lt;/p&gt;

&lt;p&gt;So I stopped.&lt;/p&gt;

&lt;p&gt;Not because someone forced me to.&lt;/p&gt;

&lt;p&gt;Because the technology made remembering unnecessary.&lt;/p&gt;




&lt;h2&gt;
  
  
  I stopped memorizing routes too
&lt;/h2&gt;

&lt;p&gt;Something similar happened with navigation.&lt;/p&gt;

&lt;p&gt;If I visited the same place often enough, I used to remember the route.&lt;/p&gt;

&lt;p&gt;Which station.&lt;/p&gt;

&lt;p&gt;Which street.&lt;/p&gt;

&lt;p&gt;Which building meant I needed to turn.&lt;/p&gt;

&lt;p&gt;Today, I can visit somewhere completely unfamiliar without thinking much about it.&lt;/p&gt;

&lt;p&gt;I open a map.&lt;/p&gt;

&lt;p&gt;If I take the wrong turn, it recalculates.&lt;/p&gt;

&lt;p&gt;If traffic changes, it finds another route.&lt;/p&gt;

&lt;p&gt;Again, this is better in almost every practical way.&lt;/p&gt;

&lt;p&gt;I don't want to go back.&lt;/p&gt;

&lt;p&gt;But there is a difference between:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I know how to get there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;My phone knows how to get there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I notice that difference whenever my battery gets low in an unfamiliar place.&lt;/p&gt;




&lt;h2&gt;
  
  
  Discovery changed too
&lt;/h2&gt;

&lt;p&gt;Restaurants are another example.&lt;/p&gt;

&lt;p&gt;There was a time when finding a place meant remembering somewhere you'd visited, hearing about it from someone, or just walking past it.&lt;/p&gt;

&lt;p&gt;Now I usually search first.&lt;/p&gt;

&lt;p&gt;Ratings.&lt;/p&gt;

&lt;p&gt;Reviews.&lt;/p&gt;

&lt;p&gt;Photos.&lt;/p&gt;

&lt;p&gt;Maps.&lt;/p&gt;

&lt;p&gt;Social media.&lt;/p&gt;

&lt;p&gt;Recommendation algorithms.&lt;/p&gt;

&lt;p&gt;The chance of making a bad choice goes down.&lt;/p&gt;

&lt;p&gt;But something else goes down too:&lt;/p&gt;

&lt;p&gt;the number of times I discover something without already knowing that other people liked it.&lt;/p&gt;

&lt;p&gt;Technology didn't remove choice.&lt;/p&gt;

&lt;p&gt;It changed how I arrive at the choice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Then AI arrived
&lt;/h2&gt;

&lt;p&gt;Contacts remember phone numbers.&lt;/p&gt;

&lt;p&gt;GPS remembers routes.&lt;/p&gt;

&lt;p&gt;Search engines retrieve information.&lt;/p&gt;

&lt;p&gt;Recommendation systems decide what might be worth our attention.&lt;/p&gt;

&lt;p&gt;Now generative AI is moving into a slightly different layer.&lt;/p&gt;

&lt;p&gt;It doesn't just remember or retrieve.&lt;/p&gt;

&lt;p&gt;It participates in the process of thinking.&lt;/p&gt;

&lt;p&gt;How should I structure this?&lt;/p&gt;

&lt;p&gt;What am I missing?&lt;/p&gt;

&lt;p&gt;How could I implement this?&lt;/p&gt;

&lt;p&gt;What are the alternatives?&lt;/p&gt;

&lt;p&gt;That might be why AI feels different.&lt;/p&gt;

&lt;p&gt;Previous tools outsourced pieces of memory and navigation.&lt;/p&gt;

&lt;p&gt;AI can outsource pieces of reasoning.&lt;/p&gt;




&lt;h2&gt;
  
  
  I'm not arguing that we should stop using it
&lt;/h2&gt;

&lt;p&gt;I use AI constantly.&lt;/p&gt;

&lt;p&gt;I use it while developing.&lt;/p&gt;

&lt;p&gt;I use it to explore ideas.&lt;/p&gt;

&lt;p&gt;I use it to find gaps in my own thinking.&lt;/p&gt;

&lt;p&gt;I use it when writing.&lt;/p&gt;

&lt;p&gt;I'm not interested in pretending that avoiding useful tools somehow makes the work more authentic.&lt;/p&gt;

&lt;p&gt;I don't carry a paper map just to prove I can navigate without GPS.&lt;/p&gt;

&lt;p&gt;But I do think there is a useful question hidden underneath all of this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What do I still want to keep as mine?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't care that I can't remember everyone's phone number.&lt;/p&gt;

&lt;p&gt;I don't care that Google Maps remembers streets better than I do.&lt;/p&gt;

&lt;p&gt;But I do care whether I can explain why I made a decision.&lt;/p&gt;

&lt;p&gt;I care whether I understand the code I'm shipping.&lt;/p&gt;

&lt;p&gt;I care whether an opinion is actually mine or something I accepted because a system gave me a convincing answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Convenience has always had a trade
&lt;/h2&gt;

&lt;p&gt;Smartphones didn't suddenly make us incapable.&lt;/p&gt;

&lt;p&gt;They made certain abilities less necessary.&lt;/p&gt;

&lt;p&gt;And when an ability becomes unnecessary often enough, we naturally practice it less.&lt;/p&gt;

&lt;p&gt;AI may be another step in the same direction.&lt;/p&gt;

&lt;p&gt;Only this time the thing becoming less necessary might not be remembering a number or memorizing a route.&lt;/p&gt;

&lt;p&gt;It might be parts of the thinking process itself.&lt;/p&gt;

&lt;p&gt;That doesn't automatically make AI bad.&lt;/p&gt;

&lt;p&gt;But it does make the boundary worth noticing.&lt;/p&gt;

&lt;p&gt;Technology rarely arrives and says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Give me this part of yourself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It gives us something useful.&lt;/p&gt;

&lt;p&gt;Something convenient.&lt;/p&gt;

&lt;p&gt;Something we quickly stop wanting to live without.&lt;/p&gt;

&lt;p&gt;And little by little, we decide what we no longer need to do ourselves.&lt;/p&gt;

&lt;p&gt;So when people ask whether AI is making us too dependent on technology, I'm starting to think the answer didn't begin with ChatGPT.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI wasn't the beginning.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We've been making this trade for a long time.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>techtalks</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Set Up a New Windows Laptop for Java and Spring Boot. Here’s What I Checked First.</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Sat, 03 Oct 2026 05:38:13 +0000</pubDate>
      <link>https://dev.to/nocklock/i-set-up-a-new-windows-laptop-for-java-and-spring-boot-heres-what-i-checked-first-5gi4</link>
      <guid>https://dev.to/nocklock/i-set-up-a-new-windows-laptop-for-java-and-spring-boot-heres-what-i-checked-first-5gi4</guid>
      <description>&lt;p&gt;I recently got a new &lt;strong&gt;LG gram Pro 17&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;My first reaction had almost nothing to do with development.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How is a 17-inch laptop this light?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The screen felt huge compared to what I was used to, and the laptop looked great on my desk.&lt;/p&gt;

&lt;p&gt;For the first few minutes, I was mostly just enjoying the fact that a 17-inch machine didn't really feel like one.&lt;/p&gt;

&lt;p&gt;But after that came the less exciting part.&lt;/p&gt;

&lt;p&gt;I had to rebuild my development environment.&lt;/p&gt;

&lt;p&gt;That reminded me of something I rarely think about on my old machine:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A lot of my development setup only feels simple because it has already been configured for years.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the old computer, Java was there.&lt;/p&gt;

&lt;p&gt;Git was there.&lt;/p&gt;

&lt;p&gt;IntelliJ already knew my projects.&lt;/p&gt;

&lt;p&gt;Dependencies had already been downloaded, and environment settings existed somewhere.&lt;/p&gt;

&lt;p&gt;I could just open a project and start working.&lt;/p&gt;

&lt;p&gt;A new laptop removes all of that.&lt;/p&gt;

&lt;p&gt;So instead of treating this as a list of programs to install, I used one simple condition to decide when the setup was actually finished:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can I take an existing Spring Boot project and run it on this machine?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  I started with Windows, not IntelliJ
&lt;/h2&gt;

&lt;p&gt;My first instinct was to install IntelliJ immediately.&lt;/p&gt;

&lt;p&gt;I didn't.&lt;/p&gt;

&lt;p&gt;Since this was a completely new Windows machine, I first finished the basic system updates.&lt;/p&gt;

&lt;p&gt;My rough order was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows Update&lt;/li&gt;
&lt;li&gt;LG driver updates&lt;/li&gt;
&lt;li&gt;Reboot&lt;/li&gt;
&lt;li&gt;Check for updates again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing interesting here.&lt;/p&gt;

&lt;p&gt;But I wanted the machine itself to be stable before adding my development tools on top of it.&lt;/p&gt;

&lt;p&gt;It also makes troubleshooting easier later.&lt;/p&gt;

&lt;p&gt;If something goes wrong, I would rather not wonder whether it came from a driver update, a Windows restart, or something I had just changed in my Java environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Then Java
&lt;/h2&gt;

&lt;p&gt;Most of the Spring projects I work with are based on Java 17, so I installed JDK 17 and checked it from the terminal first.&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;-version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is simple, but I like checking the runtime outside the IDE before doing anything else.&lt;/p&gt;

&lt;p&gt;If Java itself isn't where I expect it to be, there isn't much point debugging IntelliJ yet.&lt;/p&gt;

&lt;p&gt;I also checked the environment variables and made sure the machine was actually using the JDK version I wanted.&lt;/p&gt;

&lt;p&gt;One thing worth remembering is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Java being installed on Windows does not automatically mean your IDE or project is using the same JDK.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That becomes important a few steps later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Git came next
&lt;/h2&gt;

&lt;p&gt;After Java, I installed Git.&lt;/p&gt;

&lt;p&gt;Again, I checked it from the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point wasn't just to make &lt;code&gt;git&lt;/code&gt; available.&lt;/p&gt;

&lt;p&gt;I wanted to bring an existing repository onto the new machine instead of creating a clean demo project.&lt;/p&gt;

&lt;p&gt;A new sample project can hide problems.&lt;/p&gt;

&lt;p&gt;An old project usually doesn't.&lt;/p&gt;

&lt;p&gt;It already has dependencies, configuration, build scripts, and assumptions that have accumulated over time.&lt;/p&gt;

&lt;p&gt;That made it a much better test for the new environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Then IntelliJ
&lt;/h2&gt;

&lt;p&gt;After installing IntelliJ, one of the first things I checked was the &lt;strong&gt;Project SDK&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is the kind of setting I barely notice on a machine I've used for a long time.&lt;/p&gt;

&lt;p&gt;On a fresh install, it matters immediately.&lt;/p&gt;

&lt;p&gt;Windows may know about Java 17 while IntelliJ is still pointing somewhere else.&lt;/p&gt;

&lt;p&gt;So before changing plugins, themes, keymaps, or anything else, I made sure the project itself was using the right JDK.&lt;/p&gt;

&lt;p&gt;I also had to give IntelliJ time to do what IntelliJ does on a new project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;indexing&lt;/li&gt;
&lt;li&gt;resolving dependencies&lt;/li&gt;
&lt;li&gt;loading the project structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a moment, some things didn't behave exactly the way I expected.&lt;/p&gt;

&lt;p&gt;My first instinct was to start changing settings.&lt;/p&gt;

&lt;p&gt;Then the project finished loading.&lt;/p&gt;

&lt;p&gt;Things started behaving normally again.&lt;/p&gt;

&lt;p&gt;That was another small reminder that not every strange thing on a fresh machine is a configuration problem.&lt;/p&gt;

&lt;p&gt;Sometimes the IDE is simply still working.&lt;/p&gt;




&lt;h2&gt;
  
  
  The real test was Spring Boot
&lt;/h2&gt;

&lt;p&gt;At this point I had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JDK installed&lt;/li&gt;
&lt;li&gt;Git installed&lt;/li&gt;
&lt;li&gt;IntelliJ installed&lt;/li&gt;
&lt;li&gt;my repository cloned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That still wasn't enough.&lt;/p&gt;

&lt;p&gt;The real question was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the project run?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I opened an existing Spring Boot application, let the dependencies resolve, built it, and started the application.&lt;/p&gt;

&lt;p&gt;When the server came up normally, that was the moment I considered the basic environment ready.&lt;/p&gt;

&lt;p&gt;Not when IntelliJ opened.&lt;/p&gt;

&lt;p&gt;Not when &lt;code&gt;java -version&lt;/code&gt; worked.&lt;/p&gt;

&lt;p&gt;Not when Git cloned the repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When the application actually ran.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because a real project depends on more than an IDE.&lt;/p&gt;

&lt;p&gt;There can be build dependencies, environment variables, database settings, local files, ports, or configurations you stopped noticing a long time ago.&lt;/p&gt;

&lt;p&gt;A new machine has a nice way of exposing them.&lt;/p&gt;




&lt;h2&gt;
  
  
  A fresh laptop exposes hidden state
&lt;/h2&gt;

&lt;p&gt;Nothing I did here was technically advanced.&lt;/p&gt;

&lt;p&gt;I've installed Java before.&lt;/p&gt;

&lt;p&gt;I've installed Git before.&lt;/p&gt;

&lt;p&gt;I've configured IntelliJ before.&lt;/p&gt;

&lt;p&gt;I've run Spring Boot applications many times.&lt;/p&gt;

&lt;p&gt;But doing it again from zero made the existing environment look different.&lt;/p&gt;

&lt;p&gt;On my previous machine, years of configuration had become invisible.&lt;/p&gt;

&lt;p&gt;Everything simply worked because I had already solved those problems sometime in the past.&lt;/p&gt;

&lt;p&gt;A fresh machine removes that hidden state.&lt;/p&gt;

&lt;p&gt;And that makes a useful question easier to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How reproducible is my development environment?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't mean that every developer needs a fully automated local setup.&lt;/p&gt;

&lt;p&gt;But if I can't explain what a project needs to run on another machine, there are probably dependencies I don't understand as well as I think I do.&lt;/p&gt;

&lt;p&gt;That feels very similar to deployment.&lt;/p&gt;

&lt;p&gt;A project working on my laptop is one thing.&lt;/p&gt;

&lt;p&gt;Being able to reproduce the environment somewhere else is another.&lt;/p&gt;




&lt;h2&gt;
  
  
  This is also where shipping starts to feel different from coding
&lt;/h2&gt;

&lt;p&gt;I've run into a similar problem while building small Spring Boot projects.&lt;/p&gt;

&lt;p&gt;The main feature works.&lt;/p&gt;

&lt;p&gt;The API responds.&lt;/p&gt;

&lt;p&gt;The application runs locally.&lt;/p&gt;

&lt;p&gt;At that point, it is easy to feel like the project is almost done.&lt;/p&gt;

&lt;p&gt;Then you try to move it outside your machine.&lt;/p&gt;

&lt;p&gt;Ports matter.&lt;/p&gt;

&lt;p&gt;Environment variables matter.&lt;/p&gt;

&lt;p&gt;Configuration matters.&lt;/p&gt;

&lt;p&gt;Deployment exposes assumptions that localhost was quietly hiding.&lt;/p&gt;

&lt;p&gt;I ended up turning the checklist I was using for that process into a small guide.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ship Your Spring Boot MVP
&lt;/h3&gt;

&lt;p&gt;It's a practical checklist for checking a Spring Boot MVP before calling it ready to ship.&lt;/p&gt;

&lt;p&gt;It focuses less on writing another feature and more on things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build verification&lt;/li&gt;
&lt;li&gt;configuration&lt;/li&gt;
&lt;li&gt;environment variables&lt;/li&gt;
&lt;li&gt;deployment checks&lt;/li&gt;
&lt;li&gt;evidence that the application actually works outside your local setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://nocklock.lemonsqueezy.com/checkout/buy/587f273f-2eb7-4e55-9ad7-cfdcba6acc99" rel="noopener noreferrer"&gt;Check out Ship Your Spring Boot MVP&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I made it after realizing that getting an MVP to work and getting it ready to leave my machine were two different skills.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the LG gram Pro 17
&lt;/h2&gt;

&lt;p&gt;I'm still too early into using this laptop to call this a proper developer review.&lt;/p&gt;

&lt;p&gt;The model I'm using has a &lt;strong&gt;Core Ultra 7 and 32GB of RAM&lt;/strong&gt;, and my first impression of the 17-inch screen and weight is very positive.&lt;/p&gt;

&lt;p&gt;But I haven't spent enough time yet running my normal workload.&lt;/p&gt;

&lt;p&gt;I still want to see what happens when I have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IntelliJ&lt;/li&gt;
&lt;li&gt;Spring Boot&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;a database&lt;/li&gt;
&lt;li&gt;browser tabs&lt;/li&gt;
&lt;li&gt;documentation&lt;/li&gt;
&lt;li&gt;the rest of my usual tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;running together for hours.&lt;/p&gt;

&lt;p&gt;I also want to watch battery life and thermals during real development instead of judging them from the first day.&lt;/p&gt;

&lt;p&gt;So for now, I'm separating two things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The laptop's first impression:&lt;/strong&gt; very good.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A proper development performance review:&lt;/strong&gt; not yet.&lt;/p&gt;

&lt;p&gt;I'll write that after I've actually used it enough to have something useful to say.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm testing next
&lt;/h2&gt;

&lt;p&gt;The basic Java/Spring Boot environment is ready.&lt;/p&gt;

&lt;p&gt;Next I'll use the laptop the way I normally work:&lt;/p&gt;

&lt;p&gt;IntelliJ, Spring Boot, Docker, DB, browser tabs, documentation, and probably too many windows open at once.&lt;/p&gt;

&lt;p&gt;That's also why I chose &lt;strong&gt;32GB of RAM&lt;/strong&gt; this time.&lt;/p&gt;

&lt;p&gt;Not because Java suddenly requires 32GB.&lt;/p&gt;

&lt;p&gt;Mostly because I don't want to think about memory every time my development environment gets busy.&lt;/p&gt;

&lt;p&gt;That's another topic I'm testing separately.&lt;/p&gt;

&lt;p&gt;For now, I'm just glad the old project runs on the new machine.&lt;/p&gt;

&lt;p&gt;The laptop was new.&lt;/p&gt;

&lt;p&gt;The code wasn't.&lt;/p&gt;

&lt;p&gt;But setting everything up again made the old project look a little different.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>productivity</category>
    </item>
    <item>
      <title>AI Made Me Faster. It Also Exposed What I Didn't Understand</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Fri, 02 Oct 2026 12:55:04 +0000</pubDate>
      <link>https://dev.to/nocklock/ai-made-me-faster-it-also-exposed-what-i-didnt-understand-4mbo</link>
      <guid>https://dev.to/nocklock/ai-made-me-faster-it-also-exposed-what-i-didnt-understand-4mbo</guid>
      <description>&lt;p&gt;There was a point while building Security Lens when I realized I was fixing problems faster than I could explain them.&lt;/p&gt;

&lt;p&gt;The error disappeared.&lt;/p&gt;

&lt;p&gt;The application started.&lt;/p&gt;

&lt;p&gt;Deployment worked.&lt;/p&gt;

&lt;p&gt;Great.&lt;/p&gt;

&lt;p&gt;Then I looked at the configuration I had just changed and thought:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Wait. Why did that actually work?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That happened more than once.&lt;/p&gt;

&lt;p&gt;AI helped me move faster while building the project. So did documentation, Stack Overflow, GitHub issues, and a lot of searching.&lt;/p&gt;

&lt;p&gt;But the faster I could get an answer, the easier it became to accept a working fix before I had built a mental model of what was happening.&lt;/p&gt;

&lt;p&gt;A few small configuration problems made that painfully obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;server.port=${PORT:8080}&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I used this while deploying my Spring Boot application to Railway.&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;server.port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${PORT:8080}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first, my understanding was basically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Railway needs this.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I changed the configuration.&lt;/p&gt;

&lt;p&gt;The server started.&lt;/p&gt;

&lt;p&gt;Problem solved.&lt;/p&gt;

&lt;p&gt;Except I couldn't really explain the line.&lt;/p&gt;

&lt;p&gt;What is &lt;code&gt;PORT&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Who sets it?&lt;/p&gt;

&lt;p&gt;Why is &lt;code&gt;8080&lt;/code&gt; still there?&lt;/p&gt;

&lt;p&gt;Why does the same application use 8080 locally but something else after deployment?&lt;/p&gt;

&lt;p&gt;Once I stopped treating the line as magic, it became much simpler.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PORT&lt;/code&gt; is an environment variable.&lt;/p&gt;

&lt;p&gt;Railway provides a port to the running application.&lt;/p&gt;

&lt;p&gt;Spring Boot reads that value.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;PORT&lt;/code&gt; is not available, &lt;code&gt;8080&lt;/code&gt; becomes the fallback.&lt;/p&gt;

&lt;p&gt;So on my local machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PORT does not exist
        ↓
Spring Boot uses 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Railway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Railway provides PORT
        ↓
Spring Boot uses that value
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration wasn't really about Railway.&lt;/p&gt;

&lt;p&gt;It was about letting the environment decide something that should not be hardcoded.&lt;/p&gt;

&lt;p&gt;That was a much more useful thing to understand.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;code&gt;8081:8080&lt;/code&gt; was not just two random ports
&lt;/h2&gt;

&lt;p&gt;Docker gave me a similar moment.&lt;/p&gt;

&lt;p&gt;I was running something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 8081:8080 ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two ports.&lt;/p&gt;

&lt;p&gt;Both looked like ports.&lt;/p&gt;

&lt;p&gt;I knew the command worked.&lt;/p&gt;

&lt;p&gt;But for a while my knowledge was basically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use this and the container becomes accessible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That wasn't enough.&lt;/p&gt;

&lt;p&gt;The first port belongs to the host.&lt;/p&gt;

&lt;p&gt;The second belongs to the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;localhost:8081
      ↓
Host port 8081
      ↓
Container port 8080
      ↓
Spring Boot application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8081:8080
HOST:CONTAINER
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I understood that, another thing started making more sense.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;localhost&lt;/code&gt; is not one universal place.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;localhost&lt;/code&gt; from my PC means my PC.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;localhost&lt;/code&gt; from inside a container means that container.&lt;/p&gt;

&lt;p&gt;That sounds obvious after you understand it.&lt;/p&gt;

&lt;p&gt;It was not obvious when I was simply copying commands until something connected.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Ollama wasn't broken. Port 11434 was already being used.
&lt;/h2&gt;

&lt;p&gt;Another time, I tried to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and got an error because port &lt;code&gt;11434&lt;/code&gt; was already in use.&lt;/p&gt;

&lt;p&gt;My first reaction was basically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why isn't Ollama starting?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I restarted things.&lt;/p&gt;

&lt;p&gt;Tried the command again.&lt;/p&gt;

&lt;p&gt;Still failed.&lt;/p&gt;

&lt;p&gt;Eventually the more useful question became:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is already using port 11434?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That changed the way I looked at the problem.&lt;/p&gt;

&lt;p&gt;Ollama normally runs its local server on port &lt;code&gt;11434&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If something is already listening there, another process cannot simply bind to the same address and port.&lt;/p&gt;

&lt;p&gt;The problem was not necessarily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ollama is broken
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Process A
    ↓
already owns :11434

Process B
    ↓
tries to use :11434

    ↓

conflict
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error message had been telling me something fairly specific.&lt;/p&gt;

&lt;p&gt;I just wasn't reading it at the right level yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Environment variables were not just a deployment trick
&lt;/h2&gt;

&lt;p&gt;The same thing happened when I started handling API keys.&lt;/p&gt;

&lt;p&gt;Putting a value directly into &lt;code&gt;application.properties&lt;/code&gt; is easy.&lt;/p&gt;

&lt;p&gt;It also works.&lt;/p&gt;

&lt;p&gt;Something like this is much better:&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;openai.api.key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${OPENAI_API_KEY}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially, I thought of environment variables mostly as another thing I needed for deployment.&lt;/p&gt;

&lt;p&gt;Then I realized several concerns were connected here.&lt;/p&gt;

&lt;p&gt;The application needs configuration.&lt;/p&gt;

&lt;p&gt;Different environments may need different values.&lt;/p&gt;

&lt;p&gt;Secrets should not be committed to the repository.&lt;/p&gt;

&lt;p&gt;And the source code should not need to change just because the application moves from my machine to another environment.&lt;/p&gt;

&lt;p&gt;So this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application code
+
Environment-specific configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;started making much more sense than:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application code
+
Whatever values happen to be hardcoded on my machine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, the syntax itself wasn't difficult.&lt;/p&gt;

&lt;p&gt;The part I had been missing was the reason the boundary existed.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI made this pattern easier to notice
&lt;/h2&gt;

&lt;p&gt;AI was genuinely useful during all of this.&lt;/p&gt;

&lt;p&gt;I could paste an error.&lt;/p&gt;

&lt;p&gt;Ask what was wrong.&lt;/p&gt;

&lt;p&gt;Get a configuration example.&lt;/p&gt;

&lt;p&gt;Change the code.&lt;/p&gt;

&lt;p&gt;Try again.&lt;/p&gt;

&lt;p&gt;That made development much faster.&lt;/p&gt;

&lt;p&gt;But it also created an interesting problem.&lt;/p&gt;

&lt;p&gt;Sometimes the answer arrived before the understanding did.&lt;/p&gt;

&lt;p&gt;There is a big difference between:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have a fix.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I understand what changed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI made the first one much faster.&lt;/p&gt;

&lt;p&gt;It did not automatically give me the second one.&lt;/p&gt;

&lt;p&gt;I still had to trace the environment, the process, the port, the configuration source, or the network boundary myself.&lt;/p&gt;

&lt;p&gt;And I don't think the solution is to stop using AI.&lt;/p&gt;

&lt;p&gt;I definitely don't want to go back to doing everything slowly just to prove that I can.&lt;/p&gt;

&lt;p&gt;Instead, I'm trying to add one small step after certain fixes.&lt;/p&gt;

&lt;p&gt;When something starts working, I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Who provided this value?&lt;/p&gt;

&lt;p&gt;Which environment does this address or port belong to?&lt;/p&gt;

&lt;p&gt;What exactly would break if I removed this configuration?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I don't do this for every error.&lt;/p&gt;

&lt;p&gt;Sometimes I just need to fix something and keep building.&lt;/p&gt;

&lt;p&gt;But if the same concept keeps appearing — ports, environment variables, localhost, processes, configuration — I try not to leave it at:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It works now.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Writing about the problem exposed the gap
&lt;/h2&gt;

&lt;p&gt;The funny thing is that I noticed many of these gaps while writing development notes.&lt;/p&gt;

&lt;p&gt;While coding, this felt sufficient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error
↓
Search
↓
Change configuration
↓
Works
↓
Next
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing about it forced me into another step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error
↓
Search
↓
Change configuration
↓
Works
↓
Why?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's where I realized I couldn't clearly explain some of the fixes I had already used.&lt;/p&gt;

&lt;p&gt;So I went back.&lt;/p&gt;

&lt;p&gt;I looked at how Railway provides its port.&lt;/p&gt;

&lt;p&gt;I looked again at Docker port mapping.&lt;/p&gt;

&lt;p&gt;I checked which process was using &lt;code&gt;11434&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I thought more carefully about why configuration and secrets were coming from the environment.&lt;/p&gt;

&lt;p&gt;None of these were huge discoveries.&lt;/p&gt;

&lt;p&gt;But they started connecting.&lt;/p&gt;

&lt;p&gt;And that connection feels more valuable than memorizing another command.&lt;/p&gt;

&lt;h2&gt;
  
  
  This happened while I was building Security Lens
&lt;/h2&gt;

&lt;p&gt;Most of these examples came from building and deploying &lt;strong&gt;Security Lens&lt;/strong&gt;, a Spring Boot project that analyzes images and documents for privacy risks using OCR, EXIF metadata, rule-based detection, and an AI-assisted explanation layer.&lt;/p&gt;

&lt;p&gt;The project gave me a reason to touch deployment, containers, external services, configuration, and AI integration in one place.&lt;/p&gt;

&lt;p&gt;Apparently it also gave me several opportunities to discover things I thought I understood.&lt;/p&gt;

&lt;p&gt;I documented the architecture, implementation decisions, and what I learned in the public case study:&lt;/p&gt;

&lt;p&gt;-&amp;gt; &lt;a href="https://github.com/nocklock-h/security-lens-case-study" rel="noopener noreferrer"&gt;Security Lens — Technical Case Study on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AI is still making me faster.&lt;/p&gt;

&lt;p&gt;I just don't want to confuse faster problem solving with deeper understanding.&lt;/p&gt;

&lt;p&gt;For now, when an error disappears, I'm trying to leave myself one last question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why did it disappear?&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>ai</category>
      <category>devops</category>
    </item>
    <item>
      <title>I Used to Think Getting Better at Coding Meant Writing More Code</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Thu, 24 Sep 2026 23:47:34 +0000</pubDate>
      <link>https://dev.to/nocklock/i-used-to-think-getting-better-at-coding-meant-writing-more-code-4m54</link>
      <guid>https://dev.to/nocklock/i-used-to-think-getting-better-at-coding-meant-writing-more-code-4m54</guid>
      <description>&lt;p&gt;For a long time, I thought becoming a better developer mostly meant knowing more things.&lt;/p&gt;

&lt;p&gt;More Java.&lt;/p&gt;

&lt;p&gt;More Spring.&lt;/p&gt;

&lt;p&gt;More libraries.&lt;/p&gt;

&lt;p&gt;More tools.&lt;/p&gt;

&lt;p&gt;So whenever I felt like I wasn't improving fast enough, my answer was usually the same.&lt;/p&gt;

&lt;p&gt;I needed to study something new.&lt;/p&gt;

&lt;p&gt;Lately, I'm starting to think I had the wrong measurement.&lt;/p&gt;

&lt;h2&gt;
  
  
  I used to care mostly about whether the code worked
&lt;/h2&gt;

&lt;p&gt;When I was building something, getting the feature to work felt like the finish line.&lt;/p&gt;

&lt;p&gt;The API returned the response I expected.&lt;/p&gt;

&lt;p&gt;The button worked.&lt;/p&gt;

&lt;p&gt;The data showed up on the screen.&lt;/p&gt;

&lt;p&gt;No errors in the console.&lt;/p&gt;

&lt;p&gt;Good.&lt;/p&gt;

&lt;p&gt;Next feature.&lt;/p&gt;

&lt;p&gt;I don't think that way was necessarily wrong.&lt;/p&gt;

&lt;p&gt;When you're trying to build something, you need to make things work.&lt;/p&gt;

&lt;p&gt;But while building my recent project, Security Lens, I started noticing something.&lt;/p&gt;

&lt;p&gt;The questions I was asking had changed.&lt;/p&gt;

&lt;p&gt;Instead of only asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this work?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I started asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why does this work?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And sometimes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where will this stop working?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That second question has caused me a lot more trouble.&lt;/p&gt;

&lt;p&gt;But I think it has also taught me more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment made this painfully obvious
&lt;/h2&gt;

&lt;p&gt;I've written about this a few times recently because deployment exposed a lot of assumptions I didn't know I had.&lt;/p&gt;

&lt;p&gt;My Spring Boot application worked locally.&lt;/p&gt;

&lt;p&gt;Then I tried to move it outside my machine.&lt;/p&gt;

&lt;p&gt;Suddenly, things I barely thought about became important.&lt;/p&gt;

&lt;p&gt;Ports.&lt;/p&gt;

&lt;p&gt;Environment variables.&lt;/p&gt;

&lt;p&gt;External services.&lt;/p&gt;

&lt;p&gt;Configuration.&lt;/p&gt;

&lt;p&gt;Build behavior.&lt;/p&gt;

&lt;p&gt;All the little differences between localhost and a real deployment.&lt;/p&gt;

&lt;p&gt;At first, each one just looked like another problem to fix.&lt;/p&gt;

&lt;p&gt;Fix the port.&lt;/p&gt;

&lt;p&gt;Fix the configuration.&lt;/p&gt;

&lt;p&gt;Deploy again.&lt;/p&gt;

&lt;p&gt;But after doing this repeatedly, I noticed my behavior changing.&lt;/p&gt;

&lt;p&gt;Now when something works locally, I'm less likely to immediately think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Done.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I start wondering what made it work.&lt;/p&gt;

&lt;p&gt;Is there a local configuration I'm depending on?&lt;/p&gt;

&lt;p&gt;Is something running on my machine that I forgot about?&lt;/p&gt;

&lt;p&gt;Would another person be able to run this project?&lt;/p&gt;

&lt;p&gt;What happens if the environment changes?&lt;/p&gt;

&lt;p&gt;I definitely didn't ask all of those questions before.&lt;/p&gt;

&lt;h2&gt;
  
  
  I also started reading errors differently
&lt;/h2&gt;

&lt;p&gt;This one is harder to measure.&lt;/p&gt;

&lt;p&gt;Before, when I saw an error, my instinct was often to get rid of it as quickly as possible.&lt;/p&gt;

&lt;p&gt;Search the message.&lt;/p&gt;

&lt;p&gt;Find a similar problem.&lt;/p&gt;

&lt;p&gt;Try the solution.&lt;/p&gt;

&lt;p&gt;If it works, move on.&lt;/p&gt;

&lt;p&gt;I still search error messages, of course.&lt;/p&gt;

&lt;p&gt;Probably always will.&lt;/p&gt;

&lt;p&gt;But now I try to spend a little more time understanding what the error is actually telling me.&lt;/p&gt;

&lt;p&gt;Not because I've suddenly become extremely disciplined.&lt;/p&gt;

&lt;p&gt;Mostly because I've learned that fixing something without understanding it has a strange habit of bringing the problem back later.&lt;/p&gt;

&lt;p&gt;Sometimes in a slightly different form.&lt;/p&gt;

&lt;p&gt;And usually when I don't want it to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Even README files started looking different
&lt;/h2&gt;

&lt;p&gt;I used to think of documentation as something you wrote after development.&lt;/p&gt;

&lt;p&gt;The code was the real work.&lt;/p&gt;

&lt;p&gt;The README explained the real work.&lt;/p&gt;

&lt;p&gt;That changed when I tried to write a README that someone else could actually use.&lt;/p&gt;

&lt;p&gt;I had to explain how the application starts.&lt;/p&gt;

&lt;p&gt;What configuration it needs.&lt;/p&gt;

&lt;p&gt;What services it depends on.&lt;/p&gt;

&lt;p&gt;What someone needs before running it.&lt;/p&gt;

&lt;p&gt;And while writing those things down, I found assumptions in my own project.&lt;/p&gt;

&lt;p&gt;Things that were obvious to me only because I had built it.&lt;/p&gt;

&lt;p&gt;That was uncomfortable.&lt;/p&gt;

&lt;p&gt;But useful.&lt;/p&gt;

&lt;p&gt;Documentation wasn't just explaining my code anymore.&lt;/p&gt;

&lt;p&gt;It was testing whether I actually understood the environment around my code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The strange part is that I'm writing less code sometimes
&lt;/h2&gt;

&lt;p&gt;This is probably the biggest change.&lt;/p&gt;

&lt;p&gt;There are moments now when I spend more time checking logs, reading documentation, looking at configuration, or trying to understand a failure than actually typing code.&lt;/p&gt;

&lt;p&gt;A few years ago, that probably would have made me feel like I wasn't really developing.&lt;/p&gt;

&lt;p&gt;Now I'm not so sure.&lt;/p&gt;

&lt;p&gt;Writing code is obviously part of being a developer.&lt;/p&gt;

&lt;p&gt;But maybe the job isn't measured by how much code I can produce.&lt;/p&gt;

&lt;p&gt;Maybe it's also about how many assumptions I can notice before they become someone else's problem.&lt;/p&gt;

&lt;p&gt;I'm still learning this.&lt;/p&gt;

&lt;p&gt;And I still have plenty of moments where I change something, see that it works, and think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Great. Don't touch it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I'm definitely not claiming some dramatic transformation here.&lt;/p&gt;

&lt;p&gt;But something has changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  My questions are different now
&lt;/h2&gt;

&lt;p&gt;I don't know if I'm a better developer than I was a few months ago.&lt;/p&gt;

&lt;p&gt;That's difficult to measure from inside your own head.&lt;/p&gt;

&lt;p&gt;I still get stuck.&lt;/p&gt;

&lt;p&gt;I still search things that I feel like I should already know.&lt;/p&gt;

&lt;p&gt;I still break things.&lt;/p&gt;

&lt;p&gt;But the questions I ask while coding are definitely different.&lt;/p&gt;

&lt;p&gt;It used to be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How do I make this work?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, more often, it's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why does this work?&lt;/p&gt;

&lt;p&gt;What am I assuming?&lt;/p&gt;

&lt;p&gt;What happens when the environment changes?&lt;/p&gt;

&lt;p&gt;Could someone other than me run this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe getting better at development isn't always about knowing more answers.&lt;/p&gt;

&lt;p&gt;Sometimes it's noticing that you're starting to ask better questions.&lt;/p&gt;

&lt;p&gt;And lately, that feels more like progress to me.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>learning</category>
      <category>career</category>
      <category>webdev</category>
    </item>
    <item>
      <title>15 Things I Check Before Shipping a Spring Boot MVP</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Thu, 24 Sep 2026 05:53:43 +0000</pubDate>
      <link>https://dev.to/nocklock/15-things-i-check-before-shipping-a-spring-boot-mvp-24kg</link>
      <guid>https://dev.to/nocklock/15-things-i-check-before-shipping-a-spring-boot-mvp-24kg</guid>
      <description>&lt;p&gt;I used to think an MVP was almost finished when the main feature worked.&lt;/p&gt;

&lt;p&gt;The API responded.&lt;/p&gt;

&lt;p&gt;The UI did what I wanted.&lt;/p&gt;

&lt;p&gt;The application started without errors.&lt;/p&gt;

&lt;p&gt;On localhost, everything looked fine.&lt;/p&gt;

&lt;p&gt;Then I tried to deploy it.&lt;/p&gt;

&lt;p&gt;That was when I realized I had been checking whether my application worked, not whether it was actually ready to leave my machine.&lt;/p&gt;

&lt;p&gt;Ports behaved differently.&lt;/p&gt;

&lt;p&gt;Environment variables mattered.&lt;/p&gt;

&lt;p&gt;A service I had casually called through localhost suddenly didn't exist anymore.&lt;/p&gt;

&lt;p&gt;Something could deploy successfully and still not be usable.&lt;/p&gt;

&lt;p&gt;That experience changed the way I look at the last part of an MVP.&lt;/p&gt;

&lt;p&gt;Now, before I consider a Spring Boot project ready to ship, these are the things I check.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does the project build from a clean state?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sounds obvious.&lt;/p&gt;

&lt;p&gt;But during development, an IDE can hide a surprising amount of context.&lt;/p&gt;

&lt;p&gt;Cached dependencies, local configuration files, already-running services, and files that were never committed can all make a project look healthier than it really is.&lt;/p&gt;

&lt;p&gt;Before shipping, I want this to work without relying on my current IDE session.&lt;/p&gt;

&lt;p&gt;./gradlew clean build&lt;/p&gt;

&lt;p&gt;If the clean build fails, deployment is not my first problem yet.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Are the tests actually running?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A green build doesn't mean much if the tests I expected were never executed.&lt;/p&gt;

&lt;p&gt;I check the test output instead of only looking at:&lt;/p&gt;

&lt;p&gt;BUILD SUCCESSFUL&lt;/p&gt;

&lt;p&gt;For a small MVP, I may not have a huge test suite.&lt;/p&gt;

&lt;p&gt;That's fine.&lt;/p&gt;

&lt;p&gt;But I still want to know exactly what was tested and what was not.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can the application start without my IDE?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was one of those things I underestimated.&lt;/p&gt;

&lt;p&gt;During development, pressing the Run button in IntelliJ becomes normal.&lt;/p&gt;

&lt;p&gt;Production doesn't have an IntelliJ Run button.&lt;/p&gt;

&lt;p&gt;So I try starting the built application directly.&lt;/p&gt;

&lt;p&gt;java -jar build/libs/app.jar&lt;/p&gt;

&lt;p&gt;This often reveals configuration assumptions that were invisible during normal development.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is anything hardcoded to localhost?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This one hit me directly.&lt;/p&gt;

&lt;p&gt;Locally, code like this feels completely normal:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://localhost:11434" rel="noopener noreferrer"&gt;http://localhost:11434&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;or:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://localhost:8080" rel="noopener noreferrer"&gt;http://localhost:8080&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But localhost always means:&lt;/p&gt;

&lt;p&gt;this machine&lt;/p&gt;

&lt;p&gt;Once the application moves to another environment, "this machine" is no longer my laptop.&lt;/p&gt;

&lt;p&gt;That sounds obvious now.&lt;/p&gt;

&lt;p&gt;It wasn't always obvious while I was building.&lt;/p&gt;

&lt;p&gt;So before deployment, I search the project for things like:&lt;/p&gt;

&lt;p&gt;localhost&lt;br&gt;
127.0.0.1&lt;/p&gt;

&lt;p&gt;and ask whether each one still makes sense outside my development machine.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is the application port configurable?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Locally, I normally used a fixed Spring Boot port.&lt;/p&gt;

&lt;p&gt;Then deployment platforms forced me to think differently.&lt;/p&gt;

&lt;p&gt;Instead of assuming:&lt;/p&gt;

&lt;p&gt;server.port=8080&lt;/p&gt;

&lt;p&gt;I prefer something like:&lt;/p&gt;

&lt;p&gt;server.port=${PORT:8080}&lt;/p&gt;

&lt;p&gt;Locally, it still runs on 8080.&lt;/p&gt;

&lt;p&gt;In an environment like Railway, the platform can provide the actual port.&lt;/p&gt;

&lt;p&gt;One small configuration change removed an assumption I didn't even know I had.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Are secrets outside the repository?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;API keys and credentials should not live inside source code.&lt;/p&gt;

&lt;p&gt;That includes things like:&lt;/p&gt;

&lt;p&gt;API keys&lt;br&gt;
database passwords&lt;br&gt;
access tokens&lt;br&gt;
private credentials&lt;/p&gt;

&lt;p&gt;I now treat environment configuration as part of the deployment itself, not something to think about after deployment.&lt;/p&gt;

&lt;p&gt;A project that only works because my local .env file happens to exist isn't ready yet.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do I know every external dependency the application needs?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A Spring Boot application rarely lives alone.&lt;/p&gt;

&lt;p&gt;Mine started depending on things outside the JVM:&lt;/p&gt;

&lt;p&gt;a database, external APIs, AI services, storage, and other processes.&lt;/p&gt;

&lt;p&gt;Locally, those dependencies were already there.&lt;/p&gt;

&lt;p&gt;Production started asking a more useful question:&lt;/p&gt;

&lt;p&gt;What does this application need in order to stay alive?&lt;/p&gt;

&lt;p&gt;Writing those dependencies down helped more than I expected.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Can production actually reach those dependencies?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Knowing that a dependency exists isn't enough.&lt;/p&gt;

&lt;p&gt;The deployed application also needs to reach it.&lt;/p&gt;

&lt;p&gt;For every important dependency, I check things like:&lt;/p&gt;

&lt;p&gt;host&lt;br&gt;
port&lt;br&gt;
credentials&lt;br&gt;
network access&lt;br&gt;
timeout&lt;br&gt;
production URL&lt;/p&gt;

&lt;p&gt;This is especially important when something worked locally because another service was running on the same machine.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do I know what configuration is different in production?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Local and production environments are never completely identical.&lt;/p&gt;

&lt;p&gt;The dangerous part is not that they are different.&lt;/p&gt;

&lt;p&gt;The dangerous part is forgetting how they are different.&lt;/p&gt;

&lt;p&gt;I want to know which values change between environments.&lt;/p&gt;

&lt;p&gt;Not every value needs to be documented in a massive configuration system.&lt;/p&gt;

&lt;p&gt;But I should at least know what I'm changing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do I know which commit is actually deployed?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sounds boring until something breaks.&lt;/p&gt;

&lt;p&gt;If I deploy several times while fixing an issue, I eventually need to answer:&lt;/p&gt;

&lt;p&gt;Which version is running right now?&lt;/p&gt;

&lt;p&gt;"I think it's the latest one" is not a great answer.&lt;/p&gt;

&lt;p&gt;At minimum, I want the commit SHA or release point associated with the deployment.&lt;/p&gt;

&lt;p&gt;That single piece of information makes debugging much less vague.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does a health check pass after deployment?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A successful deployment message is useful.&lt;/p&gt;

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

&lt;p&gt;Deployment successful&lt;/p&gt;

&lt;p&gt;does not mean:&lt;/p&gt;

&lt;p&gt;Application verified&lt;/p&gt;

&lt;p&gt;I want the running service to prove that it is alive.&lt;/p&gt;

&lt;p&gt;Even a simple health endpoint is better than relying entirely on the deployment platform saying the container started.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Did I test the actual critical user flow?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This was another mindset change for me.&lt;/p&gt;

&lt;p&gt;A server being alive doesn't mean the product works.&lt;/p&gt;

&lt;p&gt;If the important flow is:&lt;/p&gt;

&lt;p&gt;Upload&lt;br&gt;
→ Analyze&lt;br&gt;
→ Process&lt;br&gt;
→ Return result&lt;/p&gt;

&lt;p&gt;then I want to test that exact flow in the deployed environment.&lt;/p&gt;

&lt;p&gt;Not only /health.&lt;/p&gt;

&lt;p&gt;Not only the home page.&lt;/p&gt;

&lt;p&gt;The part the user actually came for.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do I know where the logs are?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When something fails locally, I immediately see the console.&lt;/p&gt;

&lt;p&gt;When something fails in production, the first question becomes:&lt;/p&gt;

&lt;p&gt;Where did the error go?&lt;/p&gt;

&lt;p&gt;Before shipping, I now make sure I know how to find the application logs.&lt;/p&gt;

&lt;p&gt;It sounds like a tiny operational detail.&lt;/p&gt;

&lt;p&gt;But the first production error becomes much less stressful when I already know where to look.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do I have a rollback point?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For my early MVPs, I mostly thought about one direction:&lt;/p&gt;

&lt;p&gt;build → deploy → done&lt;/p&gt;

&lt;p&gt;Now I also think about the reverse direction.&lt;/p&gt;

&lt;p&gt;deploy → something breaks → ?&lt;/p&gt;

&lt;p&gt;I don't need an enterprise-grade rollback system for every small MVP.&lt;/p&gt;

&lt;p&gt;But I do want to know:&lt;/p&gt;

&lt;p&gt;what the previous working version was&lt;br&gt;
whether I can redeploy it&lt;br&gt;
whether database or configuration changes prevent rollback&lt;/p&gt;

&lt;p&gt;A release without a way back is more fragile than it needs to be.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What is still NOT VERIFIED?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This might be the most useful question on the list.&lt;/p&gt;

&lt;p&gt;Especially when using AI.&lt;/p&gt;

&lt;p&gt;I use AI constantly while developing now.&lt;/p&gt;

&lt;p&gt;It helps me review code, inspect configuration, find possible deployment problems, and think through things I missed.&lt;/p&gt;

&lt;p&gt;But I noticed a dangerous pattern.&lt;/p&gt;

&lt;p&gt;I could ask:&lt;/p&gt;

&lt;p&gt;Is this application production-ready?&lt;/p&gt;

&lt;p&gt;and get a very convincing answer.&lt;/p&gt;

&lt;p&gt;The problem is that AI can reason about the information I gave it.&lt;/p&gt;

&lt;p&gt;It cannot magically verify a production environment it has never seen.&lt;/p&gt;

&lt;p&gt;So I started separating these three things:&lt;/p&gt;

&lt;p&gt;VERIFIED&lt;br&gt;
INFERRED&lt;br&gt;
NOT VERIFIED&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Build passed&lt;br&gt;
→ VERIFIED&lt;/p&gt;

&lt;p&gt;This environment variable appears to be configured correctly&lt;br&gt;
→ INFERRED&lt;/p&gt;

&lt;p&gt;Production database connection works&lt;br&gt;
→ NOT VERIFIED until tested&lt;/p&gt;

&lt;p&gt;That distinction became much more valuable than asking for a generic "production readiness score."&lt;/p&gt;

&lt;p&gt;Confidence is not evidence&lt;/p&gt;

&lt;p&gt;This is probably the biggest thing I learned while turning an MVP into something I could actually deploy.&lt;/p&gt;

&lt;p&gt;A project can feel finished.&lt;/p&gt;

&lt;p&gt;AI can say the configuration looks good.&lt;/p&gt;

&lt;p&gt;The deployment platform can show a green status.&lt;/p&gt;

&lt;p&gt;None of those things automatically prove that the release works.&lt;/p&gt;

&lt;p&gt;What I want now is evidence.&lt;/p&gt;

&lt;p&gt;Did the build run?&lt;/p&gt;

&lt;p&gt;Did the application start?&lt;/p&gt;

&lt;p&gt;Did the health check pass?&lt;/p&gt;

&lt;p&gt;Did the critical flow work?&lt;/p&gt;

&lt;p&gt;Which commit was tested?&lt;/p&gt;

&lt;p&gt;What still needs verification?&lt;/p&gt;

&lt;p&gt;That is a much more useful definition of "ready" for me.&lt;/p&gt;

&lt;p&gt;I turned this into a checklist&lt;/p&gt;

&lt;p&gt;While working through these problems, I started writing the checks down instead of trying to remember them every time.&lt;/p&gt;

&lt;p&gt;I eventually turned the lightweight version into a free GitHub checklist:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/nocklock-h/spring-boot-release-checklist" rel="noopener noreferrer"&gt;👉 Spring Boot Release Checklist — Free on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It includes the main release blockers and an evidence-based way to review them.&lt;/p&gt;

&lt;p&gt;If that is enough for your project, use it.&lt;/p&gt;

&lt;p&gt;I also made a more detailed 58-page runbook for developers who want a repeatable workflow with evidence fields, failure actions, AI review prompts, deployment checks, and rollback guidance:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nocklock.lemonsqueezy.com/checkout/buy/587f273f-2eb7-4e55-9ad7-cfdcba6acc99" rel="noopener noreferrer"&gt;👉 Ship Your Spring Boot MVP — Full 58-Page Runbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I originally started writing all of this because my application worked perfectly on localhost.&lt;/p&gt;

&lt;p&gt;Deployment was what showed me how many assumptions localhost had been hiding.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>devops</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Built the MVP. Then I Realized Shipping Was a Different Skill.</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Wed, 23 Sep 2026 05:14:56 +0000</pubDate>
      <link>https://dev.to/nocklock/i-built-the-mvp-then-i-realized-shipping-was-a-different-skill-11d7</link>
      <guid>https://dev.to/nocklock/i-built-the-mvp-then-i-realized-shipping-was-a-different-skill-11d7</guid>
      <description>&lt;h1&gt;
  
  
  I Built the MVP. Then I Realized Shipping Was a Different Skill.
&lt;/h1&gt;

&lt;p&gt;A few days ago, I wrote about something I had learned the hard way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Building the MVP first and writing the README later made me realize how much I had assumed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I thought that was mostly a documentation problem.&lt;/p&gt;

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

&lt;p&gt;The deeper problem was that &lt;strong&gt;building something and shipping something are two different skills.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The MVP was working
&lt;/h2&gt;

&lt;p&gt;I recently built &lt;strong&gt;Security Lens&lt;/strong&gt;, a Spring Boot-based privacy risk analyzer for images and documents.&lt;/p&gt;

&lt;p&gt;The basic flow was working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Image Upload
      ↓
OCR
      ↓
Personal Information Detection
      ↓
EXIF Metadata Analysis
      ↓
Risk Scoring
      ↓
LLM-assisted Analysis
      ↓
Risk Explanation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It could detect things such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;phone numbers&lt;/li&gt;
&lt;li&gt;email addresses&lt;/li&gt;
&lt;li&gt;EXIF GPS information&lt;/li&gt;
&lt;li&gt;capture timestamps&lt;/li&gt;
&lt;li&gt;device metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and turn the results into a simple privacy risk score.&lt;/p&gt;

&lt;p&gt;At that point, I had something that worked.&lt;/p&gt;

&lt;p&gt;I thought I was almost done.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  "It works" was only the beginning
&lt;/h2&gt;

&lt;p&gt;Once I tried to actually ship the project, a different set of questions appeared.&lt;/p&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this method work?&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;blockquote&gt;
&lt;p&gt;Which exact version is deployed?&lt;/p&gt;

&lt;p&gt;What happens if an environment variable is missing?&lt;/p&gt;

&lt;p&gt;Did I actually verify the production configuration?&lt;/p&gt;

&lt;p&gt;What if the database or external integration behaves differently from localhost?&lt;/p&gt;

&lt;p&gt;What evidence do I have that the deployed application is healthy?&lt;/p&gt;

&lt;p&gt;Can I roll back?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those questions were not really about writing more code.&lt;/p&gt;

&lt;p&gt;They were about &lt;strong&gt;operating the thing I had already written.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment exposed assumptions
&lt;/h2&gt;

&lt;p&gt;Localhost is forgiving.&lt;/p&gt;

&lt;p&gt;Your machine already has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your environment variables&lt;/li&gt;
&lt;li&gt;your tools&lt;/li&gt;
&lt;li&gt;your configuration&lt;/li&gt;
&lt;li&gt;your ports&lt;/li&gt;
&lt;li&gt;your dependencies&lt;/li&gt;
&lt;li&gt;your development habits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Production does not care about any of that.&lt;/p&gt;

&lt;p&gt;It only gets what you actually configured.&lt;/p&gt;

&lt;p&gt;While working through deployment, I ran into issues around ports, environment setup, AI integration, and the difference between an implementation that works locally and one that can actually run as a product.&lt;/p&gt;

&lt;p&gt;That changed how I looked at "done."&lt;/p&gt;




&lt;h2&gt;
  
  
  AI made this even more interesting
&lt;/h2&gt;

&lt;p&gt;I use AI tools heavily while building.&lt;/p&gt;

&lt;p&gt;They are extremely useful.&lt;/p&gt;

&lt;p&gt;But I also noticed a dangerous pattern.&lt;/p&gt;

&lt;p&gt;It is very easy to ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Is this production-ready?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and get an answer that sounds convincing.&lt;/p&gt;

&lt;p&gt;The problem is that confidence is not evidence.&lt;/p&gt;

&lt;p&gt;So I started using a different rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the evidence is missing, the result is NOT VERIFIED.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of asking AI to make the release decision for me, I ask it to help inspect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;configuration&lt;/li&gt;
&lt;li&gt;logs&lt;/li&gt;
&lt;li&gt;build output&lt;/li&gt;
&lt;li&gt;deployment settings&lt;/li&gt;
&lt;li&gt;security assumptions&lt;/li&gt;
&lt;li&gt;missing evidence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI can be a reviewer.&lt;/p&gt;

&lt;p&gt;It should not silently become the owner of the release decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  That became a checklist
&lt;/h2&gt;

&lt;p&gt;While repeatedly asking myself the same deployment questions, I started writing them down.&lt;/p&gt;

&lt;p&gt;Eventually, those notes became a small public project:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/nocklock-h/spring-boot-release-checklist" rel="noopener noreferrer"&gt;Spring Boot Release Checklist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It focuses on things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clean builds&lt;/li&gt;
&lt;li&gt;expected tests&lt;/li&gt;
&lt;li&gt;configuration and secrets&lt;/li&gt;
&lt;li&gt;database connectivity&lt;/li&gt;
&lt;li&gt;external integrations&lt;/li&gt;
&lt;li&gt;production smoke tests&lt;/li&gt;
&lt;li&gt;rollback points&lt;/li&gt;
&lt;li&gt;evidence-first AI review&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A successful deployment is not the same as a verified release.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Then I noticed something else
&lt;/h2&gt;

&lt;p&gt;The checklist helped me catch blockers.&lt;/p&gt;

&lt;p&gt;But I still wanted a better way to answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What did I actually verify?&lt;/p&gt;

&lt;p&gt;Where is the evidence?&lt;/p&gt;

&lt;p&gt;What still remains uncertain?&lt;/p&gt;

&lt;p&gt;Should I ship this version?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That eventually became a more structured release workflow with interactive checks, evidence fields, AI review prompts, and ship / no-ship decisions.&lt;/p&gt;

&lt;p&gt;I ended up turning it into a 58-page deployment runbook.&lt;/p&gt;

&lt;p&gt;That was another new experience for me.&lt;/p&gt;

&lt;p&gt;For the first time, I was not only building software.&lt;/p&gt;

&lt;p&gt;I was taking something I had learned while building software and turning it into something another developer could actually use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Shipping forced me to learn things coding alone did not
&lt;/h2&gt;

&lt;p&gt;The interesting part was not setting up a product page.&lt;/p&gt;

&lt;p&gt;It was everything around it.&lt;/p&gt;

&lt;p&gt;I had to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what the product actually solves&lt;/li&gt;
&lt;li&gt;how to explain it in one sentence&lt;/li&gt;
&lt;li&gt;what should remain free&lt;/li&gt;
&lt;li&gt;what someone would reasonably pay for&lt;/li&gt;
&lt;li&gt;how a buyer receives the files&lt;/li&gt;
&lt;li&gt;how the download flow works&lt;/li&gt;
&lt;li&gt;what happens after payment&lt;/li&gt;
&lt;li&gt;how the public GitHub repository connects to the full version&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these problems required a complicated algorithm.&lt;/p&gt;

&lt;p&gt;But they still required engineering decisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  My definition of "done" is changing
&lt;/h2&gt;

&lt;p&gt;Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature works
→ done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Feature works
→ build succeeds
→ configuration is verified
→ deploy
→ production smoke test
→ evidence recorded
→ rollback understood
→ documentation updated
→ then maybe done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is slower.&lt;/p&gt;

&lt;p&gt;But it feels much closer to building something real.&lt;/p&gt;




&lt;h2&gt;
  
  
  One more thing I learned
&lt;/h2&gt;

&lt;p&gt;I used to think "shipping" was mostly the final step after development.&lt;/p&gt;

&lt;p&gt;Now I think it changes how you develop from the beginning.&lt;/p&gt;

&lt;p&gt;When you know you will actually deploy something, you think differently about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;configuration&lt;/li&gt;
&lt;li&gt;secrets&lt;/li&gt;
&lt;li&gt;observability&lt;/li&gt;
&lt;li&gt;failure cases&lt;/li&gt;
&lt;li&gt;reproducibility&lt;/li&gt;
&lt;li&gt;documentation&lt;/li&gt;
&lt;li&gt;users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project stops being just code.&lt;/p&gt;

&lt;p&gt;It becomes a system someone else has to trust.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;I am continuing to improve Security Lens and experimenting with the same workflow on other small products.&lt;/p&gt;

&lt;p&gt;The Security Lens source code remains private, but I created a public technical case study here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/nocklock-h/security-lens-case-study" rel="noopener noreferrer"&gt;Security Lens — Technical Case Study&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the lightweight Spring Boot release workflow is available here:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/nocklock-h/spring-boot-release-checklist" rel="noopener noreferrer"&gt;Spring Boot Release Checklist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For me, the biggest lesson so far is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Coding makes the product possible. Shipping makes it real.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Still learning.&lt;br&gt;
Still shipping.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>My Spring Boot App Worked on Localhost. Deployment Showed Me What I Had Assumed.</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Mon, 21 Sep 2026 03:28:02 +0000</pubDate>
      <link>https://dev.to/nocklock/my-spring-boot-app-worked-on-localhost-deployment-showed-me-what-i-had-assumed-9gk</link>
      <guid>https://dev.to/nocklock/my-spring-boot-app-worked-on-localhost-deployment-showed-me-what-i-had-assumed-9gk</guid>
      <description>&lt;p&gt;My application worked.&lt;/p&gt;

&lt;p&gt;The API responded.&lt;br&gt;
The frontend could call it.&lt;br&gt;
The main flow did what I expected.&lt;/p&gt;

&lt;p&gt;On my machine, at least.&lt;/p&gt;

&lt;p&gt;Then I tried to move it outside localhost.&lt;/p&gt;

&lt;p&gt;That was when I started finding things I hadn't really designed.&lt;/p&gt;

&lt;p&gt;I had only assumed them.&lt;/p&gt;

&lt;p&gt;Localhost is a very forgiving place&lt;/p&gt;

&lt;p&gt;When I was building the MVP, a lot of things were simply there.&lt;/p&gt;

&lt;p&gt;My Java environment was already configured.&lt;/p&gt;

&lt;p&gt;The right processes were running.&lt;/p&gt;

&lt;p&gt;I knew which port the application was using.&lt;/p&gt;

&lt;p&gt;When something failed, I could open IntelliJ, restart a process, change a property, and try again.&lt;/p&gt;

&lt;p&gt;None of that felt unusual.&lt;/p&gt;

&lt;p&gt;It was just my development environment.&lt;/p&gt;

&lt;p&gt;The problem was that some of those conditions had quietly become part of the application.&lt;/p&gt;

&lt;p&gt;I just hadn't written them down.&lt;/p&gt;

&lt;p&gt;For example, during development I had already run into things like:&lt;/p&gt;

&lt;p&gt;Spring Boot trying to use a port that was already occupied&lt;br&gt;
Ollama already running on localhost:11434 while I tried to start it again&lt;br&gt;
Docker becoming necessary once I wanted the application to run somewhere other than my own setup&lt;br&gt;
WSL turning what looked like a massive Git change into a CRLF/LF problem&lt;br&gt;
configuration that made perfect sense locally but needed another look before deployment&lt;/p&gt;

&lt;p&gt;None of these were particularly dramatic problems.&lt;/p&gt;

&lt;p&gt;But together, they showed me something useful.&lt;/p&gt;

&lt;p&gt;A working development environment can hide a lot of assumptions.&lt;/p&gt;

&lt;p&gt;The question changed&lt;/p&gt;

&lt;p&gt;While I was building the MVP, my main question was:&lt;/p&gt;

&lt;p&gt;Does this feature work?&lt;/p&gt;

&lt;p&gt;After I started preparing it for deployment, the question became:&lt;/p&gt;

&lt;p&gt;What needs to be true for this feature to work?&lt;/p&gt;

&lt;p&gt;That second question turned out to be much more interesting.&lt;/p&gt;

&lt;p&gt;A feature might work because:&lt;/p&gt;

&lt;p&gt;a process is already running on my machine&lt;br&gt;
a particular port happens to be available&lt;br&gt;
a local path exists&lt;br&gt;
a dependency is reachable through localhost&lt;br&gt;
an environment variable is already configured&lt;br&gt;
my machine has something installed that the server does not&lt;/p&gt;

&lt;p&gt;The code can be correct while the application still isn't ready to leave the machine it was written on.&lt;/p&gt;

&lt;p&gt;That distinction seems obvious once you see it.&lt;/p&gt;

&lt;p&gt;I don't think I really understood it until I tried to ship something myself.&lt;/p&gt;

&lt;p&gt;One small example: localhost&lt;/p&gt;

&lt;p&gt;localhost is convenient because it feels almost invisible during development.&lt;/p&gt;

&lt;p&gt;If my Spring Boot application needs another service running locally, calling something like this feels completely reasonable:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://localhost:11434" rel="noopener noreferrer"&gt;http://localhost:11434&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On my computer, it is.&lt;/p&gt;

&lt;p&gt;But the meaning of localhost changes the moment the application moves to another environment.&lt;/p&gt;

&lt;p&gt;It no longer means:&lt;/p&gt;

&lt;p&gt;my computer, where I already installed everything&lt;/p&gt;

&lt;p&gt;It means:&lt;/p&gt;

&lt;p&gt;this machine or container&lt;/p&gt;

&lt;p&gt;And the service I expected may not exist there at all.&lt;/p&gt;

&lt;p&gt;That forced me to stop treating addresses and ports as incidental details.&lt;/p&gt;

&lt;p&gt;They were part of the deployment design.&lt;/p&gt;

&lt;p&gt;Docker wasn't the goal&lt;/p&gt;

&lt;p&gt;I didn't start the project because I wanted to learn Docker.&lt;/p&gt;

&lt;p&gt;Docker appeared because I wanted somebody other than me to be able to run the application.&lt;/p&gt;

&lt;p&gt;That difference mattered to me.&lt;/p&gt;

&lt;p&gt;Learning a tool in isolation often feels abstract.&lt;/p&gt;

&lt;p&gt;But once I had a real application in front of me, the questions became concrete:&lt;/p&gt;

&lt;p&gt;What exactly needs to be inside this environment?&lt;br&gt;
What should stay outside it?&lt;br&gt;
Which port should the application expose?&lt;br&gt;
What configuration should come from the environment?&lt;br&gt;
What does the application expect to already exist?&lt;br&gt;
Can I reproduce the same startup somewhere else?&lt;/p&gt;

&lt;p&gt;Suddenly Docker wasn't a topic.&lt;/p&gt;

&lt;p&gt;It was an answer to a problem I actually had.&lt;/p&gt;

&lt;p&gt;What I check now before saying “it works”&lt;/p&gt;

&lt;p&gt;I still build the feature first.&lt;/p&gt;

&lt;p&gt;But before I consider it finished, I now try to ask a few more questions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What does this application assume is already running?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A database?&lt;/p&gt;

&lt;p&gt;A local AI server?&lt;/p&gt;

&lt;p&gt;Another API?&lt;/p&gt;

&lt;p&gt;Something I started manually an hour ago and forgot about?&lt;/p&gt;

&lt;p&gt;This question alone catches more than I expected.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is anything tied to localhost?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sometimes it should be.&lt;/p&gt;

&lt;p&gt;Sometimes it is just there because development started locally.&lt;/p&gt;

&lt;p&gt;I want to know which one it is.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Are ports part of the configuration or just part of my memory?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I had a Spring Boot application hit an 8080 port conflict and ended up running it on 8081.&lt;/p&gt;

&lt;p&gt;It was a small issue.&lt;/p&gt;

&lt;p&gt;But it reminded me that configuration I keep in my head is still configuration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Could I start this from a clean environment?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is one of the questions Docker made much harder to avoid.&lt;/p&gt;

&lt;p&gt;If the application only works because my machine has accumulated the right tools, files, processes, and settings over time, I want to know.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If it fails, will I know why?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Logs, startup errors, health checks, and clear configuration are not exciting when the application is working.&lt;/p&gt;

&lt;p&gt;They become very exciting when it isn't.&lt;/p&gt;

&lt;p&gt;Deployment started acting like a test&lt;/p&gt;

&lt;p&gt;I used to think of deployment mostly as the final step.&lt;/p&gt;

&lt;p&gt;Build the application.&lt;/p&gt;

&lt;p&gt;Finish the features.&lt;/p&gt;

&lt;p&gt;Then put it on a server.&lt;/p&gt;

&lt;p&gt;I'm starting to see it differently.&lt;/p&gt;

&lt;p&gt;Deployment is also a test of how well I understand what I built.&lt;/p&gt;

&lt;p&gt;It asks questions that localhost often doesn't.&lt;/p&gt;

&lt;p&gt;What is required?&lt;/p&gt;

&lt;p&gt;What is accidental?&lt;/p&gt;

&lt;p&gt;What is configuration?&lt;/p&gt;

&lt;p&gt;What did I assume would always be there?&lt;/p&gt;

&lt;p&gt;What only works because this is my computer?&lt;/p&gt;

&lt;p&gt;That's why some of the most useful things I learned from this MVP didn't come from adding another feature.&lt;/p&gt;

&lt;p&gt;They came from trying to move the same feature somewhere else.&lt;/p&gt;

&lt;p&gt;I'm keeping track of the small failures too&lt;/p&gt;

&lt;p&gt;I've been documenting this project while rebuilding my development habits.&lt;/p&gt;

&lt;p&gt;Not just the architecture or the successful parts.&lt;/p&gt;

&lt;p&gt;The smaller problems too:&lt;/p&gt;

&lt;p&gt;port conflicts&lt;br&gt;
Git line-ending issues&lt;br&gt;
Docker setup&lt;br&gt;
deployment configuration&lt;br&gt;
local AI dependencies&lt;br&gt;
the gap between an MVP that runs and one that can actually be used&lt;/p&gt;

&lt;p&gt;Most of these problems aren't impressive on their own.&lt;/p&gt;

&lt;p&gt;But they are exactly the things I end up searching for when I'm building something.&lt;/p&gt;

&lt;p&gt;So I'm writing them down.&lt;/p&gt;

&lt;p&gt;Mostly because I know I'll need them again.&lt;/p&gt;

&lt;p&gt;And maybe another developer will find one of them before spending an hour wondering why something that worked perfectly on localhost suddenly doesn't.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>devops</category>
      <category>learning</category>
    </item>
    <item>
      <title>I Built the MVP First. Then I Wrote the README.</title>
      <dc:creator>nocklock</dc:creator>
      <pubDate>Sun, 20 Sep 2026 12:51:48 +0000</pubDate>
      <link>https://dev.to/nocklock/i-built-the-mvp-first-then-i-wrote-the-readme-1be7</link>
      <guid>https://dev.to/nocklock/i-built-the-mvp-first-then-i-wrote-the-readme-1be7</guid>
      <description>&lt;p&gt;I did this in the wrong order.&lt;/p&gt;

&lt;p&gt;At least, that was my first thought.&lt;/p&gt;

&lt;p&gt;I was building a small project that checks images and documents for privacy risks. The backend came first, then the API, then a frontend that finally made it feel like an actual service.&lt;/p&gt;

&lt;p&gt;Somewhere along the way, I realized something.&lt;/p&gt;

&lt;p&gt;I had built quite a lot before properly organizing the requirements and documentation.&lt;/p&gt;

&lt;p&gt;Normally, I would expect the order to look more like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;requirements → design → implementation → testing → documentation&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mine looked closer to this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;build → break something → fix it → build more → test it → finally organize everything&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not exactly textbook development.&lt;/p&gt;

&lt;p&gt;But writing the documentation afterwards turned out to be more useful than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  The README became a way to inspect the project
&lt;/h2&gt;

&lt;p&gt;At first, I thought I was just writing down what I had already built.&lt;/p&gt;

&lt;p&gt;Instead, I kept finding things that were unclear.&lt;/p&gt;

&lt;p&gt;What does this API actually guarantee?&lt;/p&gt;

&lt;p&gt;What configuration is required before running the project?&lt;/p&gt;

&lt;p&gt;What happens when an external dependency is unavailable?&lt;/p&gt;

&lt;p&gt;Which parts have actually been tested?&lt;/p&gt;

&lt;p&gt;Would someone other than me know how to start this project?&lt;/p&gt;

&lt;p&gt;These are simple questions.&lt;/p&gt;

&lt;p&gt;They become much harder to ignore when you have to explain the project in plain English.&lt;/p&gt;

&lt;p&gt;And that was probably the most useful part.&lt;/p&gt;

&lt;p&gt;Documentation stopped being something I needed to finish after development.&lt;/p&gt;

&lt;p&gt;It became another form of testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI made building faster, but that created a different problem
&lt;/h2&gt;

&lt;p&gt;I also used AI quite heavily while building.&lt;/p&gt;

&lt;p&gt;ChatGPT, Claude, and coding tools made it much easier to move from an idea to working code.&lt;/p&gt;

&lt;p&gt;That part was great.&lt;/p&gt;

&lt;p&gt;But faster implementation also meant I could move past decisions without really stopping to organize them.&lt;/p&gt;

&lt;p&gt;A configuration works, so I move on.&lt;/p&gt;

&lt;p&gt;An endpoint returns the right response, so I move on.&lt;/p&gt;

&lt;p&gt;Docker starts successfully, so I move on.&lt;/p&gt;

&lt;p&gt;Then deployment comes.&lt;/p&gt;

&lt;p&gt;Suddenly all the small things I skipped become visible again.&lt;/p&gt;

&lt;p&gt;Environment variables.&lt;/p&gt;

&lt;p&gt;Ports.&lt;/p&gt;

&lt;p&gt;Database configuration.&lt;/p&gt;

&lt;p&gt;Build commands.&lt;/p&gt;

&lt;p&gt;Health checks.&lt;/p&gt;

&lt;p&gt;Logs.&lt;/p&gt;

&lt;p&gt;Security settings.&lt;/p&gt;

&lt;p&gt;Things that looked like small details on localhost started feeling much more important once I tried to get the application outside my own machine.&lt;/p&gt;

&lt;p&gt;That was the point where I started thinking less about:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Does this code work?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and more about:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Is this actually ready to leave localhost?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is a bigger difference between those two questions than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  A working MVP is not the same as a shippable MVP
&lt;/h2&gt;

&lt;p&gt;I used to think of an MVP mainly in terms of features.&lt;/p&gt;

&lt;p&gt;If the core function works, the MVP works.&lt;/p&gt;

&lt;p&gt;I still think that is partly true.&lt;/p&gt;

&lt;p&gt;But there is another stage after that.&lt;/p&gt;

&lt;p&gt;The application has to survive being run somewhere other than your laptop.&lt;/p&gt;

&lt;p&gt;Someone needs to know how to configure it.&lt;/p&gt;

&lt;p&gt;You need to know when it has failed.&lt;/p&gt;

&lt;p&gt;Secrets should not be sitting in the repository.&lt;/p&gt;

&lt;p&gt;The build should be reproducible.&lt;/p&gt;

&lt;p&gt;The production environment should not depend on something you happened to configure manually three days ago and already forgot about.&lt;/p&gt;

&lt;p&gt;None of these things are particularly exciting.&lt;/p&gt;

&lt;p&gt;They are also exactly the things I kept running into.&lt;/p&gt;

&lt;p&gt;So I started writing them down.&lt;/p&gt;

&lt;p&gt;Not as another tutorial.&lt;/p&gt;

&lt;p&gt;Just as a checklist I wanted for myself the next time I reached this stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  That checklist became its own project
&lt;/h2&gt;

&lt;p&gt;Eventually those notes turned into &lt;strong&gt;Ship Your Spring Boot MVP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Eventually those notes turned into a small release workflow.&lt;/p&gt;

&lt;p&gt;I made a lightweight version public on GitHub:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/nocklock-h/spring-boot-release-checklist" rel="noopener noreferrer"&gt;Spring Boot Release Checklist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a practical pre-release checklist for Spring Boot MVPs moving from localhost to production.&lt;/p&gt;

&lt;p&gt;The idea is simple:&lt;/p&gt;

&lt;p&gt;before calling an MVP ready, go through the things that are easy to miss when most of your attention has been on making the feature work.&lt;/p&gt;

&lt;p&gt;I also ended up expanding those notes into a more detailed Full Edition with more checks, verification steps, deployment notes, and the things I wish I had organized earlier.&lt;/p&gt;

&lt;p&gt;I later expanded the same idea into a more structured 58-page deployment runbook, but the GitHub checklist remains the lightweight public version.&lt;/p&gt;

&lt;p&gt;I did not originally plan to make a product out of it.&lt;/p&gt;

&lt;p&gt;It came from repeatedly asking myself the same question while building:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“What am I forgetting before I ship this?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  I still don't think building first is the correct order
&lt;/h2&gt;

&lt;p&gt;I would not turn this into advice like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Forget requirements. Just start coding.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That would be the wrong lesson.&lt;/p&gt;

&lt;p&gt;If anything, doing it backwards showed me why the earlier steps exist.&lt;/p&gt;

&lt;p&gt;But there was something valuable about documenting a project after I had actually struggled with it.&lt;/p&gt;

&lt;p&gt;The checklist was no longer theoretical.&lt;/p&gt;

&lt;p&gt;Every item reminded me of something I had needed, forgotten, broken, or had to go back and check.&lt;/p&gt;

&lt;p&gt;Maybe next time my process will be a little cleaner.&lt;/p&gt;

&lt;p&gt;Probably not perfectly clean.&lt;/p&gt;

&lt;p&gt;And that's fine.&lt;/p&gt;

&lt;p&gt;I'm still learning by making things.&lt;/p&gt;




&lt;p&gt;I keep more notes about development, AI tools, deployment, and the things that break along the way on the &lt;strong&gt;&lt;a href="https://nocklock.tistory.com/" rel="noopener noreferrer"&gt;Nocklock blog&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can also see the projects I'm building on &lt;strong&gt;&lt;a href="https://github.com/nocklock-h" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>devops</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
