<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Software Engineering on PG Blog</title><link>https://pg-blogs.netlify.app/tags/software-engineering/</link><description>Recent content in Software Engineering 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/tags/software-engineering/index.xml" rel="self" type="application/rss+xml"/><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>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>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>