<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on PG Blog</title><link>https://pg-blogs.netlify.app/posts/</link><description>Recent content in Posts on PG Blog</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 03 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://pg-blogs.netlify.app/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Avoiding ORM Traps and the N+1 Problem in Java</title><link>https://pg-blogs.netlify.app/posts/8-avoiding-orm-traps-and-n-plus-1-in-java/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/8-avoiding-orm-traps-and-n-plus-1-in-java/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;An ORM like JPA/Hibernate makes database access feel like working with plain objects. That is exactly the problem: a single innocent-looking loop can fire hundreds of SQL queries, and you won&amp;rsquo;t see it until production slows to a crawl.&lt;/p&gt;
&lt;p&gt;This post covers the ORM traps that bite Java teams most often — starting with the notorious &lt;strong&gt;N+1 query problem&lt;/strong&gt; — and the concrete techniques that fix them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-n1-problem"&gt;The N+1 Problem&lt;/h2&gt;
&lt;p&gt;Consider loading authors and printing their books:&lt;/p&gt;</description></item><item><title>Avoiding ORM Traps and the N+1 Problem in Python</title><link>https://pg-blogs.netlify.app/posts/9-avoiding-orm-traps-and-n-plus-1-in-python/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/9-avoiding-orm-traps-and-n-plus-1-in-python/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Python&amp;rsquo;s ORMs — SQLAlchemy and the Django ORM — are a joy to write and dangerously easy to make slow. A loop over a queryset that reads one related field can silently fire hundreds of queries. It looks fine in development and falls over on real data.&lt;/p&gt;
&lt;p&gt;This post covers the ORM traps Python teams hit most, starting with the &lt;strong&gt;N+1 query problem&lt;/strong&gt;, in both SQLAlchemy and Django.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="the-n1-problem"&gt;The N+1 Problem&lt;/h2&gt;
&lt;p&gt;You load authors and print each author&amp;rsquo;s book count:&lt;/p&gt;</description></item><item><title>Building Agentic Workflows in Java</title><link>https://pg-blogs.netlify.app/posts/14-building-agentic-workflows-in-java/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/14-building-agentic-workflows-in-java/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;Agent&amp;rdquo; has become the word for any program that calls an LLM more than once, which makes it a word worth being precise about. An agent, in the sense this post uses, is a loop: the model decides which tool to call next, your code executes it, and the result feeds back in — repeating until the model decides it&amp;rsquo;s done. That&amp;rsquo;s a genuinely different (and riskier) shape than a single request/response call.&lt;/p&gt;</description></item><item><title>Building Agentic Workflows in Python</title><link>https://pg-blogs.netlify.app/posts/15-building-agentic-workflows-in-python/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/15-building-agentic-workflows-in-python/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&amp;ldquo;Agent&amp;rdquo; has become the word for any program that calls an LLM more than once, which makes it a word worth being precise about. An agent, in the sense this post uses, is a loop: the model decides which tool to call next, your code executes it, and the result feeds back in — repeating until the model decides it&amp;rsquo;s done. That&amp;rsquo;s a genuinely different (and riskier) shape than a single request/response call.&lt;/p&gt;</description></item><item><title>Building Reliable LLM Applications in Java</title><link>https://pg-blogs.netlify.app/posts/11-building-reliable-llm-apps-in-java/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/11-building-reliable-llm-apps-in-java/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;LLMs are usually associated with Python, but a great deal of production software — banking, enterprise backends, long-lived services — runs on the JVM, and those systems increasingly need to call language models too. Java&amp;rsquo;s strong typing and mature tooling are genuine assets here: they push you toward exactly the discipline reliable LLM applications require.&lt;/p&gt;
&lt;p&gt;The core mindset is the same in any language: &lt;strong&gt;treat model output as a hypothesis to verify, not a fact to trust.&lt;/strong&gt; This post covers the practices that make Java LLM applications production-grade, using Anthropic&amp;rsquo;s Claude and the official &lt;code&gt;anthropic-java&lt;/code&gt; SDK.&lt;/p&gt;</description></item><item><title>Building Reliable LLM Applications in Python</title><link>https://pg-blogs.netlify.app/posts/10-building-reliable-llm-apps-in-python/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/10-building-reliable-llm-apps-in-python/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Calling an LLM API is easy. Building an application on top of one that is &lt;em&gt;reliable&lt;/em&gt; — that fails predictably, doesn&amp;rsquo;t hallucinate its way into wrong answers, and doesn&amp;rsquo;t surprise you with a bill — is a real engineering discipline.&lt;/p&gt;
&lt;p&gt;The core mindset shift: &lt;strong&gt;treat model output as a hypothesis to verify, not a fact to trust.&lt;/strong&gt; This post covers the practices that make Python LLM applications production-grade, using Anthropic&amp;rsquo;s Claude and the official &lt;code&gt;anthropic&lt;/code&gt; SDK.&lt;/p&gt;</description></item><item><title>Database Indexing and Query Optimization for Java Developers</title><link>https://pg-blogs.netlify.app/posts/18-database-indexing-and-query-optimization-for-java/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/18-database-indexing-and-query-optimization-for-java/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Fixing N+1 queries (see the &lt;a href="https://pg-blogs.netlify.app/posts/8-avoiding-orm-traps-and-n-plus-1-in-java/"&gt;previous post&lt;/a&gt;) gets your Hibernate app down to a handful of queries per request. The next bottleneck is what each of those queries costs once your tables have millions of rows — and that is almost always a question of indexing.&lt;/p&gt;
&lt;p&gt;An index turns &amp;ldquo;scan every row&amp;rdquo; into &amp;ldquo;look it up directly.&amp;rdquo; Get the index wrong — or skip it — and a query that took 2ms in development takes 4 seconds in production once real data volume shows up.&lt;/p&gt;</description></item><item><title>Database Indexing and Query Optimization for Python Developers</title><link>https://pg-blogs.netlify.app/posts/19-database-indexing-and-query-optimization-for-python/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/19-database-indexing-and-query-optimization-for-python/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Fixing N+1 queries with &lt;code&gt;select_related&lt;/code&gt;/&lt;code&gt;prefetch_related&lt;/code&gt; or &lt;code&gt;selectinload&lt;/code&gt; (see the &lt;a href="https://pg-blogs.netlify.app/posts/9-avoiding-orm-traps-and-n-plus-1-in-python/"&gt;previous post&lt;/a&gt;) gets you down to a small, sane number of queries per request. The next bottleneck is what each query costs once the table has millions of rows — and that is almost always about indexing.&lt;/p&gt;
&lt;p&gt;An index turns &amp;ldquo;scan every row&amp;rdquo; into &amp;ldquo;look it up directly.&amp;rdquo; Skip it, and a query that&amp;rsquo;s instant in development takes seconds once real data volume shows up in production.&lt;/p&gt;</description></item><item><title>Designing for Change: Boundaries, Contracts, and Dependency Inversion in Java</title><link>https://pg-blogs.netlify.app/posts/12-designing-for-change-in-java/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/12-designing-for-change-in-java/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Most Java systems don&amp;rsquo;t fail because a single class was written badly. They fail because the &lt;em&gt;boundaries&lt;/em&gt; between classes were drawn in the wrong place — a database library leaks into business logic, a payment provider&amp;rsquo;s SDK shows up in twelve unrelated files, and changing one third-party dependency means touching half the codebase.&lt;/p&gt;
&lt;p&gt;Designing for change is not about predicting the future correctly. It&amp;rsquo;s about &lt;strong&gt;isolating what varies&lt;/strong&gt; so that when it inevitably does vary — a new payment provider, a swapped database, a different message broker — the blast radius is one adapter, not the whole system.&lt;/p&gt;</description></item><item><title>Designing for Change: Boundaries, Contracts, and Dependency Inversion in Python</title><link>https://pg-blogs.netlify.app/posts/13-designing-for-change-in-python/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/13-designing-for-change-in-python/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Python&amp;rsquo;s flexibility makes it easy to wire everything together directly — call the &lt;code&gt;requests&lt;/code&gt; library from inside your business logic, import the ORM model straight into your pricing rules, instantiate a third-party SDK client wherever it&amp;rsquo;s needed. It works, right up until the payment provider changes, or you want a fast unit test that doesn&amp;rsquo;t need a live database.&lt;/p&gt;
&lt;p&gt;Designing for change means drawing boundaries around the parts of the system likely to move — third-party services, storage, transport — so that a swap or a test double touches one small adapter instead of rippling through the codebase.&lt;/p&gt;</description></item><item><title>Error Handling Best Practices in Java</title><link>https://pg-blogs.netlify.app/posts/4-error-handling-best-practices-in-java/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/4-error-handling-best-practices-in-java/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Error handling is where a lot of otherwise-good Java code quietly goes wrong. Exceptions get swallowed, &lt;code&gt;null&lt;/code&gt; leaks across boundaries, and stack traces arrive with no context about &lt;em&gt;what the program was trying to do&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Good error handling is not about catching everything. It is about being deliberate: fail fast on programmer errors, recover gracefully from expected failures, and never lose the information a future on-call engineer will need.&lt;/p&gt;</description></item><item><title>Error Handling Best Practices in Python</title><link>https://pg-blogs.netlify.app/posts/5-error-handling-best-practices-in-python/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/5-error-handling-best-practices-in-python/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Python&amp;rsquo;s error handling reads deceptively simply — &lt;code&gt;try&lt;/code&gt;, &lt;code&gt;except&lt;/code&gt;, done. But the difference between code that fails clearly and code that fails mysteriously comes down to a handful of habits: catching the &lt;em&gt;right&lt;/em&gt; exception, preserving the original cause, and knowing when &lt;em&gt;not&lt;/em&gt; to raise at all.&lt;/p&gt;
&lt;p&gt;This post covers the practices that keep Python failures debuggable in production.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="prefer-eafp-over-look-before-you-leap"&gt;Prefer EAFP over Look-Before-You-Leap&lt;/h2&gt;
&lt;p&gt;Python idiom favors &lt;strong&gt;EAFP&lt;/strong&gt; — &amp;ldquo;Easier to Ask Forgiveness than Permission.&amp;rdquo; Rather than checking whether an operation will succeed, attempt it and handle the failure:&lt;/p&gt;</description></item><item><title>Production-Grade Docker Images for Java</title><link>https://pg-blogs.netlify.app/posts/6-production-grade-docker-images-for-java/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/6-production-grade-docker-images-for-java/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;A Java &lt;code&gt;Dockerfile&lt;/code&gt; can be three lines and technically work — or it can be a 700 MB image that runs as root, rebuilds from scratch on every code change, and gets OOM-killed under load. The difference is a handful of well-understood practices.&lt;/p&gt;
&lt;p&gt;This post walks through building Java images that are &lt;strong&gt;small, secure, cache-friendly, and container-aware&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="start-from-a-slim-current-jre--not-a-jdk"&gt;Start From a Slim, Current JRE — Not a JDK&lt;/h2&gt;
&lt;p&gt;You need a JDK to &lt;em&gt;build&lt;/em&gt;, but only a JRE to &lt;em&gt;run&lt;/em&gt;. Shipping a full JDK bloats the image and widens the attack surface.&lt;/p&gt;</description></item><item><title>Production-Grade Docker Images for Python</title><link>https://pg-blogs.netlify.app/posts/7-production-grade-docker-images-for-python/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/7-production-grade-docker-images-for-python/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;The typical first-draft Python &lt;code&gt;Dockerfile&lt;/code&gt; — &lt;code&gt;FROM python&lt;/code&gt;, &lt;code&gt;COPY . .&lt;/code&gt;, &lt;code&gt;pip install -r requirements.txt&lt;/code&gt; — produces an image that is close to a gigabyte, reinstalls every dependency whenever a single source file changes, and runs as root with a Python process that ignores &lt;code&gt;SIGTERM&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This post covers how to fix all of that: &lt;strong&gt;small images, cached dependency installs, a non-root runtime, and clean shutdowns.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="pick-the-right-base-image"&gt;Pick the Right Base Image&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;python:3.12-slim&lt;/code&gt; is the pragmatic default: a Debian-based image with Python but without the large build toolchain. Avoid the full &lt;code&gt;python:3.12&lt;/code&gt; (hundreds of MB of tools you don&amp;rsquo;t run) and be cautious with &lt;code&gt;alpine&lt;/code&gt; — its &lt;code&gt;musl&lt;/code&gt; libc frequently breaks or slows down packages with C extensions (NumPy, pandas, database drivers), forcing slow source builds.&lt;/p&gt;</description></item><item><title>Testing Best Practices in Java</title><link>https://pg-blogs.netlify.app/posts/16-testing-best-practices-in-java/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/16-testing-best-practices-in-java/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Most Java codebases have plenty of tests and still get burned by production bugs. The problem is rarely &lt;em&gt;quantity&lt;/em&gt; — it&amp;rsquo;s that the tests exercise the wrong things: they assert internal wiring instead of behavior, they mock away every collaborator until nothing real is left, or they cover the happy path ten times and the failure path zero times.&lt;/p&gt;
&lt;p&gt;Good tests are fast, deterministic, and tell you something true about behavior. This post covers the practices that make JUnit 5 and Mockito tests actually earn their keep.&lt;/p&gt;</description></item><item><title>Testing Best Practices in Python</title><link>https://pg-blogs.netlify.app/posts/17-testing-best-practices-in-python/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/17-testing-best-practices-in-python/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Python&amp;rsquo;s testing tools are lightweight enough that it&amp;rsquo;s easy to write a lot of tests without writing &lt;em&gt;good&lt;/em&gt; ones. A suite that mocks every collaborator, duplicates the same assertion ten times with different inputs pasted in by hand, or chases a coverage number will pass in CI and still miss real bugs.&lt;/p&gt;
&lt;p&gt;pytest gives you fixtures, &lt;code&gt;parametrize&lt;/code&gt;, and &lt;code&gt;monkeypatch&lt;/code&gt; — the tools that make it just as easy to write the &lt;em&gt;right&lt;/em&gt; tests as the wrong ones. This post covers how to use them well.&lt;/p&gt;</description></item><item><title>Java and the JVM Ecosystem: Kotlin, Scala, and Beyond</title><link>https://pg-blogs.netlify.app/posts/3-java-and-the-jvm-ecosystem/</link><pubDate>Tue, 06 Jan 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/3-java-and-the-jvm-ecosystem/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Java is often discussed as a single programming language.&lt;/p&gt;
&lt;p&gt;In reality, Java is part of something much larger: the &lt;strong&gt;Java Virtual Machine (JVM) ecosystem&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The JVM has become a powerful runtime for multiple languages, each solving different problems while sharing the same platform.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="what-makes-the-jvm-special"&gt;What Makes the JVM Special&lt;/h2&gt;
&lt;p&gt;The JVM provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Platform independence&lt;/li&gt;
&lt;li&gt;Automatic memory management&lt;/li&gt;
&lt;li&gt;Just-in-time compilation&lt;/li&gt;
&lt;li&gt;Mature tooling and debuggers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Languages built on the JVM inherit these benefits without needing to reimplement them.&lt;/p&gt;</description></item><item><title>Java in the Age of Cloud and Microservices</title><link>https://pg-blogs.netlify.app/posts/2-java-in-the-age-of-cloud-and-microservices/</link><pubDate>Tue, 06 Jan 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/2-java-in-the-age-of-cloud-and-microservices/</guid><description>&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;
&lt;p&gt;For a long time, Java was associated with large, monolithic enterprise applications running on heavyweight application servers.&lt;/p&gt;
&lt;p&gt;Then the industry shifted toward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Microservices&lt;/li&gt;
&lt;li&gt;Containers&lt;/li&gt;
&lt;li&gt;Cloud-native architectures&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many assumed Java would struggle to adapt.&lt;/p&gt;
&lt;p&gt;Instead, Java evolved — and in many cases, &lt;strong&gt;thrived&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="from-monoliths-to-microservices"&gt;From Monoliths to Microservices&lt;/h2&gt;
&lt;p&gt;Early Java enterprise applications often relied on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Large EAR/WAR deployments&lt;/li&gt;
&lt;li&gt;Heavy application servers&lt;/li&gt;
&lt;li&gt;Centralized databases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Modern Java architectures look very different:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Small, focused services&lt;/li&gt;
&lt;li&gt;Independent deployments&lt;/li&gt;
&lt;li&gt;Stateless APIs&lt;/li&gt;
&lt;li&gt;Horizontal scalability&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Frameworks like &lt;strong&gt;Spring Boot&lt;/strong&gt; dramatically simplified Java service development by removing boilerplate and enabling rapid startup.&lt;/p&gt;</description></item><item><title>Why Java Still Matters Today (Even in the Age of New Languages)</title><link>https://pg-blogs.netlify.app/posts/1-why-java-still-matters-today/</link><pubDate>Mon, 05 Jan 2026 00:00:00 +0000</pubDate><guid>https://pg-blogs.netlify.app/posts/1-why-java-still-matters-today/</guid><description>&lt;h2 id="introduction-isnt-java-old"&gt;Introduction: Isn’t Java… Old?&lt;/h2&gt;
&lt;p&gt;Every few years, a new programming language becomes the &lt;em&gt;next big thing&lt;/em&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python for data science&lt;/li&gt;
&lt;li&gt;JavaScript everywhere&lt;/li&gt;
&lt;li&gt;Go for cloud-native systems&lt;/li&gt;
&lt;li&gt;Rust for memory safety&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And inevitably, someone asks:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“Does Java still matter today?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The short answer is &lt;strong&gt;yes&lt;/strong&gt;.&lt;br&gt;
The long answer is &lt;strong&gt;yes — for more reasons than most people realize&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Java is no longer the “boring enterprise language” people love to mock. In fact, Java has quietly evolved while continuing to power &lt;strong&gt;some of the world’s largest and most critical systems&lt;/strong&gt;.&lt;/p&gt;</description></item></channel></rss>