<?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: lixy</title>
    <description>The latest articles on DEV Community by lixy (@li_5408).</description>
    <link>https://dev.to/li_5408</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%2F4098113%2F5934119d-601c-4e2f-bc0a-efaddc6fd2fe.png</url>
      <title>DEV Community: lixy</title>
      <link>https://dev.to/li_5408</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/li_5408"/>
    <language>en</language>
    <item>
      <title>The Paycheck Math to Run Before You Resign</title>
      <dc:creator>lixy</dc:creator>
      <pubDate>Tue, 15 Sep 2026 03:56:08 +0000</pubDate>
      <link>https://dev.to/li_5408/the-paycheck-math-to-run-before-you-resign-11am</link>
      <guid>https://dev.to/li_5408/the-paycheck-math-to-run-before-you-resign-11am</guid>
      <description>&lt;p&gt;My last employer owed me 1,731 dollars for vacation I never took, and I only know the number because a coworker told me to check. The final pay stub listed it as one line among others, and I had nearly signed off without reading it. Developers switch jobs often enough that this math should be routine, yet almost nobody runs it before the exit interview.&lt;/p&gt;

&lt;p&gt;So here it is: the three calculations that decide what your last paycheck contains, with worked numbers you can replicate in a spreadsheet in about ten minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your salary has a daily rate, and it decides the last check
&lt;/h2&gt;

&lt;p&gt;Salaried employees are paid for the week, not the day. When you leave mid pay period, somebody in payroll has to convert your salary into a daily rate before the partial period can be paid. The standard conversion runs through the workweek: annual salary divided by 52, then by 5 working days. Divided by 260, in other words.&lt;/p&gt;

&lt;p&gt;At 72,000 dollars a year, that is 276.92 dollars per day. Resign effective the 15th of the month, with semi-monthly pay, and payroll counts your workdays from the 1st to the 15th, weekends excluded, then multiplies. Eleven workdays at 276.92 comes to 3,046.15 dollars.&lt;/p&gt;

&lt;p&gt;Here is the part that surprises people. A full semi-monthly check at that salary is 3,000 dollars, so the partial check is larger than a full one. The first half of that particular month held more workdays than the fixed halves of the year assume. Proration follows the calendar you actually worked, not the neat slices your salary was quoted in.&lt;/p&gt;

&lt;p&gt;Knowing your daily rate before you resign means you can predict the last check to the dollar, which means you can spot the week the payroll system forgot. A colleague's first final-check draft was missing three days, and the correction took one email because he could show the arithmetic instead of a feeling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unused PTO is cash on the table
&lt;/h2&gt;

&lt;p&gt;The 1,731 dollars from my opening line was a PTO payout, and the number is not arbitrary. Vacation accrues as you work. With a 120-hour annual allowance and monthly accrual, leaving after 8 full months gives you 120 divided by 12, times 8, which is 80 hours accrued. Take 30 of them during the year and 50 hours remain.&lt;/p&gt;

&lt;p&gt;Then the balance converts to cash. For salaried staff the hourly equivalent is annual salary divided by 2,080, the standard 40 hours times 52 weeks. At 72,000 dollars that is 34.62 an hour, and 50 hours pays 1,731 dollars. The whole calculation is three divisions and a subtraction.&lt;/p&gt;

&lt;p&gt;One warning before you bank on it: whether that payout arrives at all depends on your state. There is no federal requirement to pay out unused vacation. California, Colorado, Illinois, Massachusetts and a handful of other states treat accrued PTO as wages that must be paid. Elsewhere, a use-it-or-lose-it policy in the handbook can legally zero out your balance on your last day. Know which group your state is in before you choose between burning your remaining days in the notice period and cashing them out.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the money has to show up
&lt;/h2&gt;

&lt;p&gt;The final check also has a legal deadline, and the deadlines vary more than most people expect. In California, with at least 72 hours of notice, it is due on your last day, and without notice within 72 hours after it. Miss it and the employer owes waiting-time penalties that accrue at a full day of wages per day, capped at 30 days. Oregon is nearly as strict. Most states, including Texas, simply require payment by the next regular payday, which can leave you waiting two weeks.&lt;/p&gt;

&lt;p&gt;Hourly readers get one extra line of protection: overtime earned in the prior period that had not yet been paid must ride on the final check, not the one after it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the numbers before the exit interview
&lt;/h2&gt;

&lt;p&gt;Exit weeks are rushed. You are handing over repos, writing docs nobody will read, and sitting through goodbyes. The one hour you spend on this math beforehand is the difference between signing a correct final check and chasing payroll from your new job.&lt;/p&gt;

&lt;p&gt;All three calculations are pro rata at heart: a salary sliced into days, an allowance sliced into months, a balance converted at an hourly rate. If you want to check your own figures without building the spreadsheet, the &lt;a href="https://www.proratatool.com" rel="noopener noreferrer"&gt;free pro rata calculator&lt;/a&gt; does the divisions, and the &lt;a href="https://www.proratatool.com/blog/prorated-final-paycheck" rel="noopener noreferrer"&gt;prorated final paycheck guide&lt;/a&gt; covers the state deadlines and the full worked examples, including what else belongs on the check besides salary.&lt;/p&gt;

&lt;p&gt;Your last paycheck is the one document at a job you will never get corrected by asking nicely in a standup. Read it before you sign, and make sure the arithmetic was on your side.&lt;/p&gt;

</description>
      <category>career</category>
      <category>money</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The Invoice Math Most Freelance Developers Learn Too Late</title>
      <dc:creator>lixy</dc:creator>
      <pubDate>Tue, 08 Sep 2026 02:42:37 +0000</pubDate>
      <link>https://dev.to/li_5408/the-invoice-math-most-freelance-developers-learn-too-late-503n</link>
      <guid>https://dev.to/li_5408/the-invoice-math-most-freelance-developers-learn-too-late-503n</guid>
      <description>&lt;p&gt;My first invoice had no payment terms at all. It said the amount, my bank details, and nothing about when payment was due. The client paid after 47 days. I remember checking the account every morning for a week and a half, wondering whether asking about it would seem pushy.&lt;/p&gt;

&lt;p&gt;That was the year I learned that payment terms are not paperwork. They are pricing. Here is the math I wish someone had walked me through before I sent that first invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Net 30 means you are the bank
&lt;/h2&gt;

&lt;p&gt;Net 30 reads like a neutral deadline: pay within 30 days. Look at it from the other side of the table. Your client has received the work. You have received a promise. For those 30 days, you have issued an interest-free loan to a company that is probably larger than you.&lt;/p&gt;

&lt;p&gt;Big companies know this perfectly well. Corporate finance teams track days sales outstanding the way engineers track latency, and stretching it is free money for them. A procurement department that pushes a small vendor from Net 30 to Net 60 has effectively borrowed from that vendor at zero percent, and it shows up as an efficiency win in their annual report.&lt;/p&gt;

&lt;p&gt;None of this means you should refuse Net 30. It is the market default, and fighting it on every contract costs goodwill. The point is to know what you are trading. When a client asks to move from Net 30 to Net 75, that request has a price, and you are the one paying it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2/10 net 30 trick, and what it really pays
&lt;/h2&gt;

&lt;p&gt;One client offered me a term I had never seen: 2/10 net 30. It means: if I pay within 10 days, take a 2 percent discount. Otherwise the full amount is due in 30 days.&lt;/p&gt;

&lt;p&gt;I almost ignored it. Two percent of a four-figure invoice sounded like a rounding error. Then I ran the numbers.&lt;/p&gt;

&lt;p&gt;A 1000 dollar invoice with that term gives the client two choices: pay 980 within 10 days, or pay 1000 at day 30. The 20 dollar difference buys 20 extra days of credit. Annualize that, using the banker's 360-day convention that invoice math traditionally uses, and the cost of those 20 days is 2/98 multiplied by 360/20, which is about 36.7 percent per year.&lt;/p&gt;

&lt;p&gt;That number deserves a second read. The client's alternative sources of short-term money, a credit line, a factor, an overdraft, all cost a fraction of that. So when a client offers you 2/10 net 30 as the vendor, they are handing you a paying proposition on a plate, and you should nearly always take it. When you offer it to your clients, you are buying fast cash at a steep implied rate, which can still be the right call. Cash you have today is cash you can bill against on the next project, and for a one-person shop, velocity often beats margin.&lt;/p&gt;

&lt;p&gt;The mistake is not choosing either side. The mistake is treating 2 percent as too small to think about, when it is actually 36.7 percent a year wearing a disguise.&lt;/p&gt;

&lt;h2&gt;
  
  
  A due date without a penalty is a suggestion
&lt;/h2&gt;

&lt;p&gt;The same client who paid in 47 days? The second invoice had "Net 14" on it. It changed nothing, because I had no consequence attached.&lt;/p&gt;

&lt;p&gt;Late fee clauses fix this. The standard formulation is 1.5 percent per month on overdue balances, which compounds to roughly 19.6 percent a year. Two things surprised me about that number. First, it is not arbitrary: it sits high enough to sting and low enough to stay inside the usury caps that several US states place on late charges. Second, you generally cannot invent a punitive rate after the fact. If the clause was not on the invoice the client agreed to, enforcing it becomes an argument instead of arithmetic.&lt;/p&gt;

&lt;p&gt;In practice I have collected a late fee exactly once. That is fine. The clause works the way a good fence works. Most people respect it, and the one time someone tests it, the conversation starts from your document instead of your feelings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invoice numbers are cheap insurance
&lt;/h2&gt;

&lt;p&gt;The last piece is the one I resisted longest, because it looks like bureaucracy. Invoice numbers should be sequential, unique, and never reused, even across years and clients.&lt;/p&gt;

&lt;p&gt;The reasons are all downstream. When a tax office asks you to reconcile a quarter, sequential numbers let you prove no gaps exist. When a client disputes a charge, "invoice 0042" is an unambiguous reference for both sides. When two clients merge and their accounting departments compare records, a clean series survives the audit. Freelancers who reuse numbers or restart them every January eventually buy back the difference in hours, usually during the worst possible week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it on paper
&lt;/h2&gt;

&lt;p&gt;None of this helps if it lives in your head at deadline time. The terms that matter fit on three lines at the bottom of an invoice: the due date, the discount if you offer one, and the late fee. When I finally started writing invoices that carried all three, my average time-to-payment dropped from that 47-day embarrassment to under three weeks, with no difficult conversation at all.&lt;/p&gt;

&lt;p&gt;If you want to see the terms side by side with real numbers, or generate an invoice that already has the slots for them, I keep a &lt;a href="https://www.invmakr.com" rel="noopener noreferrer"&gt;free invoice maker&lt;/a&gt; for exactly this: fill in the fields, get a clean PDF, no account. For the longer version of the discount math, including when it is worth refusing, the &lt;a href="https://www.invmakr.com/blog/early-payment-discounts" rel="noopener noreferrer"&gt;early payment discount breakdown&lt;/a&gt; covers the cases this post skipped.&lt;/p&gt;

&lt;p&gt;Payment terms are not admin. They are the part of your rate that decides when you actually get paid, and sometimes whether you get paid at all. Learn the math once, put it on every invoice, and let the document do the enforcing.&lt;/p&gt;

</description>
      <category>freelance</category>
      <category>business</category>
      <category>finance</category>
    </item>
    <item>
      <title>I Ran My Own Favicon Checker Against 10 Sites. All 10 Failed.</title>
      <dc:creator>lixy</dc:creator>
      <pubDate>Sat, 05 Sep 2026 15:38:21 +0000</pubDate>
      <link>https://dev.to/li_5408/i-ran-my-own-favicon-checker-against-10-sites-all-10-failed-5840</link>
      <guid>https://dev.to/li_5408/i-ran-my-own-favicon-checker-against-10-sites-all-10-failed-5840</guid>
      <description>&lt;p&gt;I maintain a small collection of single-purpose web tools. Last month I built a favicon checker: you type a URL, it reads the icon declarations in the HTML head, probes every referenced file, and also hits &lt;code&gt;/favicon.ico&lt;/code&gt; directly, because plenty of software still requests that path without ever reading your HTML.&lt;/p&gt;

&lt;p&gt;The first thing you should do with any auditing tool is point it at your own stuff. So I did. Ten sites, all built by me, all shipped and verified in browsers I actually use.&lt;/p&gt;

&lt;p&gt;All ten failed. Not seven out of ten. Ten.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure one: SVG-only icon sets
&lt;/h2&gt;

&lt;p&gt;Every site had a nice crisp &lt;code&gt;favicon.svg&lt;/code&gt; and nothing else. Modern browsers request it, render it at any size, everything looks great in Chrome and Firefox. Then something older comes along: a bookmark sidebar, an RSS reader, a corporate proxy portal that lists your link, that one intern running Opera 12. These clients do not parse your &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags. They request &lt;code&gt;/favicon.ico&lt;/code&gt; and hope.&lt;/p&gt;

&lt;p&gt;All ten sites returned a 404 for that path. The fix is not glamorous. You need an actual &lt;code&gt;.ico&lt;/code&gt; file, ideally with 16, 32, and 48 pixel frames packed inside, plus a PNG for iOS. More on that below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure two: no apple-touch-icon
&lt;/h2&gt;

&lt;p&gt;Nine of the ten sites had no &lt;code&gt;apple-touch-icon.png&lt;/code&gt;. When someone saves such a site to an iOS home screen, Safari does not use your favicon. It takes a screenshot of the page, letterboxes it, and calls that your app icon. If you have ever seen a bookmark that looked like a cropped text paragraph, that is why.&lt;/p&gt;

&lt;p&gt;The fix is one file and one tag: a 180 by 180 PNG, referenced with &lt;code&gt;&amp;lt;link rel="apple-touch-icon" href="/apple-touch-icon.png"&amp;gt;&lt;/code&gt;. Done. No JavaScript, no media queries, no dark mode variants needed. iOS rounds the corners itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure three: the 404 that would not leave
&lt;/h2&gt;

&lt;p&gt;This is the one that cost me an evening, so pay attention if any of your sites sit behind Cloudflare.&lt;/p&gt;

&lt;p&gt;I generated the missing icons, deployed them, and re-ran the checker. Still 404. I deployed again. Still 404. I started doubting my build, my deploy script, my cache headers, my sanity.&lt;/p&gt;

&lt;p&gt;The files were fine. The problem was that Cloudflare had cached the original 404 responses. The &lt;code&gt;.ico&lt;/code&gt; extension is on Cloudflare's default cached file list, and that rule does not distinguish between a 200 with an image and a 404 with an error page. The edge had helpfully stored my failure and now served it at speed.&lt;/p&gt;

&lt;p&gt;You can confirm this with a response header check: if &lt;code&gt;cf-cache-status: HIT&lt;/code&gt; shows up on a request you know should be fresh, you are looking at a cached answer, not the origin's. Two ways out: request the file with a query string like &lt;code&gt;?v=2&lt;/code&gt;, which bypasses the cache and shows you what the origin actually returns, or wait for the cached entry to expire on its own. Purging works too if you have dashboard access.&lt;/p&gt;

&lt;p&gt;So the deploy sequence that actually worked was: upload the files, verify with a query string, then trust that the plain path would heal itself as the cache aged out.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I generated everything locally
&lt;/h2&gt;

&lt;p&gt;No online favicon service, because the source icons belong to the sites and uploading them somewhere for conversion defeats the point. The pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Render the source SVG in a headless browser with a transparent background, at each target size. This respects whatever the SVG actually draws, which matters for icons with effects that naive rasterizers mangle.&lt;/li&gt;
&lt;li&gt;Use Pillow to pack 16, 32, and 48 pixel frames into a single multi-resolution &lt;code&gt;.ico&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Save a 180 by 180 PNG as &lt;code&gt;apple-touch-icon.png&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add the tag block to the HTML head of every page template.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then the checklist before calling it done: &lt;code&gt;/favicon.ico&lt;/code&gt; returns 200 with an image content type, &lt;code&gt;apple-touch-icon.png&lt;/code&gt; exists, and the sizes declared in the HTML match the actual pixel dimensions of the files. That last one is sneaky. I found two sites declaring &lt;code&gt;sizes="32x32"&lt;/code&gt; on a file that was actually 48 pixels square. Browsers mostly cope, but a checker that compares declared versus actual will flag it, and the fix is a one-line attribute edit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five minute version for your own sites
&lt;/h2&gt;

&lt;p&gt;If you want to audit your own setup without building anything, run through this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;curl -I https://yoursite.example/favicon.ico&lt;/code&gt; and look for &lt;code&gt;200&lt;/code&gt; plus an image content type, not &lt;code&gt;text/html&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check that &lt;code&gt;/apple-touch-icon.png&lt;/code&gt; exists and is 180 by 180&lt;/li&gt;
&lt;li&gt;Compare the &lt;code&gt;sizes&lt;/code&gt; attributes in your head against the real dimensions of the files they point to&lt;/li&gt;
&lt;li&gt;If you use a CDN, remember it may cache your 404s the same way it caches your assets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or point a checker at it. I eventually wired the probe logic into a &lt;a href="https://www.devtoolcore.com/tools/favicon-checker" rel="noopener noreferrer"&gt;free favicon checker&lt;/a&gt; that runs the whole battery in one go: the direct &lt;code&gt;/favicon.ico&lt;/code&gt; request, every declared icon, the apple-touch-icon, format sniffing, and declared versus actual dimensions. It runs server-side probes only, no account, and it is how the ten-versus-zero result above got measured. For a deeper reference on sizes, formats, and which tags to use, the &lt;a href="https://www.devtoolcore.com/blog/favicon-best-practices" rel="noopener noreferrer"&gt;favicon best practices guide&lt;/a&gt; on the same site covers the long version.&lt;/p&gt;

&lt;p&gt;The humbling part of shipping an auditing tool is that it audits you first. All ten of my sites failed a check I had just written the logic for. The icons took an afternoon to fix. The Cloudflare cache lesson would have taken much longer to debug in production, and that one I only hit because the checker kept retesting instead of trusting my deploy script's success message.&lt;/p&gt;

&lt;p&gt;Tools that re-verify from the outside are worth more than tools that report what you intended.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>favicon</category>
    </item>
    <item>
      <title>Four Java Bugs That Bad Indentation Hides in Plain Sight</title>
      <dc:creator>lixy</dc:creator>
      <pubDate>Thu, 27 Aug 2026 22:45:59 +0000</pubDate>
      <link>https://dev.to/li_5408/four-java-bugs-that-bad-indentation-hides-in-plain-sight-4jng</link>
      <guid>https://dev.to/li_5408/four-java-bugs-that-bad-indentation-hides-in-plain-sight-4jng</guid>
      <description>&lt;p&gt;javac does not care what your code looks like. Whitespace carries zero meaning in the Java Language Specification, so the compiler reads your file as a stream of tokens and skips every space you typed. Fine for the machine. A problem for you, because indentation is the part of the code that talks to humans. When the layout says one thing and the braces say another, the reader trusts the layout, and the bug ships.&lt;/p&gt;

&lt;p&gt;Here are four bugs I have either shipped, reviewed, or nearly missed. Every snippet compiles, and in every one the indentation is lying.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The extra semicolon that turns a condition into a no-op&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;java&lt;br&gt;
if (cart.total() &amp;gt; FREE_SHIPPING_MIN);&lt;br&gt;
    applyFreeShipping(cart);&lt;br&gt;
chargeCustomer(cart);&lt;br&gt;
The layout says: carts above the threshold get free shipping. The semicolon right after the condition says otherwise. A lone semicolon is a complete, empty statement, so it becomes the entire if body, and applyFreeShipping runs for every order, threshold or not.&lt;/p&gt;

&lt;p&gt;The same trap exists under for and while: for (Item item : items); is a loop that iterates over everything and does nothing, then the indented line below it runs once. Static analysis knows this pattern (SonarQube rule S1116, IDE inspections flag it), but only when someone runs it.&lt;/p&gt;

&lt;p&gt;Fixed:&lt;/p&gt;

&lt;p&gt;java&lt;br&gt;
if (cart.total() &amp;gt; FREE_SHIPPING_MIN) {&lt;br&gt;
    applyFreeShipping(cart);&lt;br&gt;
}&lt;br&gt;
chargeCustomer(cart);&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The dangling else that picks the wrong if&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;java&lt;br&gt;
if (customer.isVip())&lt;br&gt;
    if (order.isGiftWrapped())&lt;br&gt;
        includeGiftCard(order);&lt;br&gt;
else&lt;br&gt;
    chargeUpgradeFee(order);&lt;br&gt;
Read it the way the indentation suggests: VIP orders get a gift card, everyone else pays an upgrade fee. Java binds else to the nearest if, so the fee is actually charged to VIP customers whose order has no gift wrap. Non-VIP customers never reach the else at all.&lt;/p&gt;

&lt;p&gt;java&lt;br&gt;
if (customer.isVip()) {&lt;br&gt;
    if (order.isGiftWrapped()) {&lt;br&gt;
        includeGiftCard(order);&lt;br&gt;
    } else {&lt;br&gt;
        chargeUpgradeFee(order);&lt;br&gt;
    }&lt;br&gt;
}&lt;br&gt;
Same tokens, different program. Braces settle ownership of the else. Indentation never can.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The closing brace that ends the block one line early&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;java&lt;br&gt;
if (!response.isOk()) {&lt;br&gt;
    log.warn("bad response, will retry");&lt;br&gt;
}&lt;br&gt;
    retry(request);&lt;br&gt;
This reads as "log and retry on failure", but the brace after the log line already closed the if. retry runs on every request, success or not. The only thing claiming it is conditional is the indentation, and indentation has no vote.&lt;/p&gt;

&lt;p&gt;This one shows up after refactors: someone deletes a line, the closing brace lands one row too high, and nobody re-formats the block. With the brace parked at the right margin and the layout untouched, code review walks right past it.&lt;/p&gt;

&lt;p&gt;java&lt;br&gt;
if (!response.isOk()) {&lt;br&gt;
    log.warn("bad response, will retry");&lt;br&gt;
    retry(request);&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The unbraced guard chain, a.k.a. the goto fail shape&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;java&lt;br&gt;
if (session == null)&lt;br&gt;
    return false;&lt;br&gt;
if (!session.signatureValid())&lt;br&gt;
    return false;&lt;br&gt;
    revokeSession(session.userId());&lt;br&gt;
return grantAccess(session);&lt;br&gt;
Every line under the guards looks like part of the chain. Only the first two are. revokeSession belongs to no if at all, so it runs on every login attempt, including successful ones, right before access is granted. When the session is null it does not just run pointlessly, it throws.&lt;/p&gt;

&lt;p&gt;The shape has a famous ancestor: CVE-2014-1266, the Apple goto fail bug. One duplicated line under an unbraced if chain in C skipped certificate verification on every SSL connection in iOS for over a year. Different language, same mechanism: unbraced guards plus a little drift equals a security bug that compiles.&lt;/p&gt;

&lt;p&gt;java&lt;br&gt;
if (session == null) {&lt;br&gt;
    return false;&lt;br&gt;
}&lt;br&gt;
if (!session.signatureValid()) {&lt;br&gt;
    revokeSession(session.userId());&lt;br&gt;
    return false;&lt;br&gt;
}&lt;br&gt;
return grantAccess(session);&lt;br&gt;
Why Java puts up with this&lt;br&gt;
Python made indentation part of the syntax, so the layout cannot lie. Java moved all structure into braces and semicolons and left the layout as free space. The trade is that you can format code any way you like, including ways that mislead. Indentation ends up working like a comment written in whitespace, and comments rot.&lt;/p&gt;

&lt;p&gt;Let a machine own the layout&lt;br&gt;
The fix for all four bugs is not reading harder. People read less carefully under deadline, not more. The fix is giving ownership of layout to a formatter so no human re-indents by hand and drift cannot survive:&lt;/p&gt;

&lt;p&gt;Format on save: IntelliJ save actions, or format on save in VS Code. The file stays in canonical shape, so a misplaced brace becomes visible immediately.&lt;br&gt;
Enforce it in CI: Spotless, Checkstyle, or fmt-maven-plugin failing the build. Drift stops at the pull request instead of production.&lt;br&gt;
Quick one-offs: for a snippet from a code review or a machine without your IDE configured, paste it into &lt;a href="https://javafmt.com" rel="noopener noreferrer"&gt;JavaFmt, a free online Java formatter&lt;/a&gt;. It runs in the browser, code never leaves the tab, and it applies the classic four-space conventions.&lt;br&gt;
If you want the longer background: &lt;a href="https://javafmt.com/blog/does-indentation-matter-in-java" rel="noopener noreferrer"&gt;does indentation matter in Java&lt;/a&gt; covers why teams standardize at all, and &lt;a href="https://javafmt.com/blog/java-line-wrapping-rules" rel="noopener noreferrer"&gt;Java line wrapping rules&lt;/a&gt; covers the part of the layout that formatters still argue about.&lt;/p&gt;

&lt;p&gt;Three habits, in order of impact: always brace single statements, format on save, fail the build on drift. The compiler will never care about your indentation. Make something that does.&lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
