<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Engineering leadership for complex technology problems. on dplabs — Software Engineering &amp; Technology Consultancy</title>
		<link>https://dplabs.tech/</link>
		<description>Recent content in Engineering leadership for complex technology problems. on dplabs — Software Engineering &amp; Technology Consultancy</description>
		<generator>Hugo</generator>
		<language>en-us</language>
		
		
		
		
			<lastBuildDate>Tue, 28 Jul 2026 00:00:00 +0000</lastBuildDate>
		
			<atom:link href="https://dplabs.tech/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>Trunk-Based Development vs GitFlow: What the Debate Is Actually About</title>
				<link>https://dplabs.tech/blog/trunk-based-vs-gitflow/</link>
				<pubDate>Tue, 28 Jul 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/trunk-based-vs-gitflow/</guid>
				<description>&lt;p&gt;The branching strategy debate is loudly opinionated in both directions. GitFlow advocates point to clean release management and isolated feature development. Trunk-based development advocates point to the evidence that short-lived branches correlate with higher engineering performance. Both sides are often talking past each other.&lt;/p&gt;&#xA;&lt;p&gt;The useful question is not &amp;ldquo;which is better&amp;rdquo; but &amp;ldquo;which fits our context&amp;rdquo; — and the context includes team size, release cadence, engineering practices, and the nature of the software being delivered.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The Cost of Clever Code</title>
				<link>https://dplabs.tech/blog/cost-of-clever-code/</link>
				<pubDate>Mon, 13 Jul 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/cost-of-clever-code/</guid>
				<description>&lt;p&gt;Every programming language has features that allow you to write dense, sophisticated-looking code. Java has streams, lambdas, method references, and generic type hierarchies. These are useful tools. They&amp;rsquo;re also regularly used to write code that is harder to understand than the straightforward version with no corresponding benefit.&lt;/p&gt;&#xA;&lt;p&gt;Clever code has a cost. The cost is paid by every engineer who reads it, modifies it, or debugs it. In a living codebase, that cost is paid repeatedly.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Secure by Default: APIs That Are Hard to Misuse</title>
				<link>https://dplabs.tech/blog/secure-by-default-apis/</link>
				<pubDate>Mon, 29 Jun 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/secure-by-default-apis/</guid>
				<description>&lt;p&gt;Security by default is a design principle, not a feature list. An API is secure by default when using it correctly produces secure behavior automatically, and producing insecure behavior requires deliberate effort.&lt;/p&gt;&#xA;&lt;p&gt;Most security failures are not the result of sophisticated attacks on well-designed systems. They&amp;rsquo;re the result of developers using APIs incorrectly, making incorrect assumptions about what defaults are safe, or forgetting to add security checks that should have been built in.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Designing Code for Change</title>
				<link>https://dplabs.tech/blog/designing-code-for-change/</link>
				<pubDate>Mon, 15 Jun 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/designing-code-for-change/</guid>
				<description>&lt;p&gt;Software changes. This is not a contingency — it&amp;rsquo;s the nature of software development. Requirements evolve. Systems scale. Technologies improve. Teams change. The useful question is not &amp;ldquo;will this change?&amp;rdquo; but &amp;ldquo;when it changes, how expensive will the change be?&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Code that&amp;rsquo;s designed for change is not over-engineered. It&amp;rsquo;s code where the natural boundaries of the design align with the natural boundaries of change. When a change is required, it affects a small, predictable, well-bounded area. When it&amp;rsquo;s not, the design hasn&amp;rsquo;t added unnecessary complexity &amp;ldquo;just in case.&amp;rdquo;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Infrastructure as Code Is Software</title>
				<link>https://dplabs.tech/blog/infrastructure-as-code-is-software/</link>
				<pubDate>Mon, 01 Jun 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/infrastructure-as-code-is-software/</guid>
				<description>&lt;p&gt;Infrastructure as Code emerged as a response to the problem of undocumented, inconsistent, manually-managed infrastructure. Click-ops servers where nobody knows what&amp;rsquo;s actually configured. Snowflake environments that differ from production in undocumented ways. Infrastructure that can&amp;rsquo;t be reproduced.&lt;/p&gt;&#xA;&lt;p&gt;IaC solved these problems by treating infrastructure as code. The irony is that many teams write infrastructure code with none of the engineering rigor they apply to application code. No reviews, no tests, no code quality standards, no documentation. The result: IaC that has the same problems as the click-ops it replaced, plus the added complexity of Terraform state files.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Immutable Infrastructure vs Configuration Management: Choosing Your Operations Model</title>
				<link>https://dplabs.tech/blog/immutable-infrastructure/</link>
				<pubDate>Mon, 18 May 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/immutable-infrastructure/</guid>
				<description>&lt;p&gt;Two schools of thought in infrastructure management have different answers to the same question: when the configuration of a server needs to change, what do you do?&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Configuration management&lt;/strong&gt; answer: connect to the server and change it. Tools like Ansible, Puppet, and Chef automate this process.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Immutable infrastructure&lt;/strong&gt; answer: build a new server with the desired configuration, route traffic to it, terminate the old one. The server itself is never modified after provisioning.&lt;/p&gt;</description>
			</item>
			<item>
				<title>How to Evaluate Architecture Without Falling in Love With Solutions</title>
				<link>https://dplabs.tech/blog/how-to-evaluate-architecture/</link>
				<pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/how-to-evaluate-architecture/</guid>
				<description>&lt;p&gt;Every engineer who has spent significant time in architecture discussions has seen the same dynamic: a proposed solution is presented, its proponents defend it, critics pick at details, and the group gravitates toward whichever solution has the most enthusiastic advocate rather than the best trade-off profile.&lt;/p&gt;&#xA;&lt;p&gt;This is not evaluation. It&amp;rsquo;s preference arbitration.&lt;/p&gt;&#xA;&lt;p&gt;Effective architecture review requires a framework that separates the emotional investment from the analysis — that forces explicit engagement with dimensions like cost, risk, and reversibility that advocates tend to minimize and critics tend to ignore.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The Security Risks of AI-Enabled Applications</title>
				<link>https://dplabs.tech/blog/security-risks-ai-applications/</link>
				<pubDate>Tue, 21 Apr 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/security-risks-ai-applications/</guid>
				<description>&lt;p&gt;Integrating AI into applications introduces security risks that traditional application security doesn&amp;rsquo;t address. Most security frameworks were designed for deterministic systems. LLMs are not deterministic — their outputs depend on inputs in ways that are difficult to predict, test, or constrain.&lt;/p&gt;&#xA;&lt;p&gt;This doesn&amp;rsquo;t mean AI applications are inherently insecure. It means you need to think about security differently when LLMs are in the loop.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-core-problem-llms-process-text-as-instructions&#34;&gt;The Core Problem: LLMs Process Text as Instructions&lt;/h2&gt;&#xA;&lt;p&gt;In a conventional application, there&amp;rsquo;s a clear distinction between code and data. A SQL injection attack tries to blur that boundary — to get user input interpreted as SQL commands. You prevent it by parameterizing queries, by treating user input as data rather than code.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Building AI Features Without Losing Control of Your Architecture</title>
				<link>https://dplabs.tech/blog/ai-features-architecture/</link>
				<pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/ai-features-architecture/</guid>
				<description>&lt;p&gt;AI features are getting added to production systems quickly — often quickly enough that the usual architectural discipline gets skipped. The integration is direct: OpenAI SDK in the service, API calls scattered through business logic, prompts hardcoded in the calling code.&lt;/p&gt;&#xA;&lt;p&gt;This works for a proof of concept. At production scale, with real operational requirements, it creates a class of problems that are expensive to fix after the fact.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-vendor-lock-in-problem&#34;&gt;The Vendor Lock-in Problem&lt;/h2&gt;&#xA;&lt;p&gt;Directly using the OpenAI SDK, the Anthropic SDK, or any provider&amp;rsquo;s client library throughout your codebase creates tight coupling to that provider&amp;rsquo;s interface. When you want to:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Building Reliable AI Agents: Beyond the Demo</title>
				<link>https://dplabs.tech/blog/building-reliable-ai-agents/</link>
				<pubDate>Mon, 23 Mar 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/building-reliable-ai-agents/</guid>
				<description>&lt;p&gt;AI agents are compelling in demos. A language model that can use tools, retrieve information, take actions, and chain multiple steps together appears to solve problems that weren&amp;rsquo;t solvable before. Then you try to ship one and discover that the demo success rate was 80%, which means 1 in 5 operations fail silently.&lt;/p&gt;&#xA;&lt;p&gt;Production agents require the same engineering discipline as any other distributed system. The failure modes are different, but the principles — observability, error handling, testing, graceful degradation — are the same ones you apply everywhere else.&lt;/p&gt;</description>
			</item>
			<item>
				<title>AI Engineering Is Software Engineering</title>
				<link>https://dplabs.tech/blog/ai-engineering-is-software-engineering/</link>
				<pubDate>Mon, 09 Mar 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/ai-engineering-is-software-engineering/</guid>
				<description>&lt;p&gt;There&amp;rsquo;s a tendency to treat AI features as different from other software. Different enough that normal engineering practices don&amp;rsquo;t apply, or apply differently, or can be deferred. This tendency produces AI systems that are unreliable, unobservable, expensive to operate, and difficult to improve.&lt;/p&gt;&#xA;&lt;p&gt;AI systems built with the same engineering discipline as other distributed systems are more reliable, cheaper to operate, and easier to improve. The disciplines aren&amp;rsquo;t different. The application is.&lt;/p&gt;</description>
			</item>
			<item>
				<title>MCP: The Interface Between AI Models and the Rest of Your Systems</title>
				<link>https://dplabs.tech/blog/mcp-model-context-protocol/</link>
				<pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/mcp-model-context-protocol/</guid>
				<description>&lt;p&gt;The Model Context Protocol (MCP) is a specification introduced by Anthropic in November 2024 that standardizes how LLM applications connect to external data sources and tools. It has since been adopted broadly — by OpenAI, Google, and major developer tooling vendors — and is becoming the standard interface layer between AI systems and the services they interact with.&lt;/p&gt;&#xA;&lt;p&gt;Understanding MCP requires understanding the problem it solves, because the protocol itself is not complex.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Multi-Agent Systems: Useful Architecture or Complexity for Its Own Sake?</title>
				<link>https://dplabs.tech/blog/multi-agent-systems/</link>
				<pubDate>Mon, 09 Feb 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/multi-agent-systems/</guid>
				<description>&lt;p&gt;Multi-agent systems have become a popular architectural pattern in AI applications. The idea is appealing: if one agent is good, multiple agents working together must be better. Specialized agents for different subtasks. Agents reviewing each other&amp;rsquo;s work. Parallel execution across agents.&lt;/p&gt;&#xA;&lt;p&gt;The appeal is intuitive. The implementation is often a mess of non-deterministic coordination, cascading hallucinations, and debugging nightmares where a problem in one agent produces a subtly wrong result that propagates through three others before producing a visible failure.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Evaluating LLM Applications: Beyond Vibe Checks</title>
				<link>https://dplabs.tech/blog/evaluating-llm-applications/</link>
				<pubDate>Mon, 26 Jan 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/evaluating-llm-applications/</guid>
				<description>&lt;p&gt;Most teams evaluate their LLM applications by asking them a few questions and deciding whether the answers look right. This is not evaluation — it&amp;rsquo;s a vibe check. It doesn&amp;rsquo;t scale, doesn&amp;rsquo;t catch regressions, and doesn&amp;rsquo;t provide any basis for measuring improvement over time.&lt;/p&gt;&#xA;&lt;p&gt;Systematic LLM evaluation is harder than evaluating deterministic software. The outputs are probabilistic, quality is multidimensional, and the correct answer often isn&amp;rsquo;t a single string. These are difficulties, not reasons to skip evaluation.&lt;/p&gt;</description>
			</item>
			<item>
				<title>RAG vs Fine-Tuning: Choosing the Right Tool for LLM Customization</title>
				<link>https://dplabs.tech/blog/rag-vs-fine-tuning/</link>
				<pubDate>Mon, 12 Jan 2026 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/rag-vs-fine-tuning/</guid>
				<description>&lt;p&gt;&amp;ldquo;Should we use RAG or fine-tuning?&amp;rdquo; is a question teams ask when they need an LLM to work better for their specific use case. It&amp;rsquo;s often the wrong question — they&amp;rsquo;re different tools that solve different problems and can be combined.&lt;/p&gt;&#xA;&lt;p&gt;Getting this wrong is expensive. Fine-tuning a model to &amp;ldquo;know&amp;rdquo; information that should be retrieved leads to a model that&amp;rsquo;s expensive to update and confidently answers with stale data. Implementing RAG when the problem is actually about model behavior leads to a system that retrieves correctly but still behaves wrong.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Database Migrations in Continuous Delivery</title>
				<link>https://dplabs.tech/blog/database-migrations-continuous-delivery/</link>
				<pubDate>Mon, 04 Aug 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/database-migrations-continuous-delivery/</guid>
				<description>&lt;p&gt;Every CI/CD guide covers application deployment. Very few cover the harder problem: database schema changes in a system that can never be offline.&lt;/p&gt;&#xA;&lt;p&gt;The challenge is that application and database deployments are not atomic. During a rolling or blue-green deployment, old and new application versions run simultaneously against the same database. A migration that&amp;rsquo;s incompatible with the old version causes the old version to fail — precisely during the deployment window when you can least afford failures.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Spring Boot Dependency Injection Without Losing Your Architecture</title>
				<link>https://dplabs.tech/blog/spring-boot-dependency-injection/</link>
				<pubDate>Mon, 21 Jul 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/spring-boot-dependency-injection/</guid>
				<description>&lt;p&gt;Spring&amp;rsquo;s dependency injection container is one of the most mature and capable DI frameworks available. It&amp;rsquo;s also capable of enabling architectures that are deeply coupled to Spring internals, impossible to test without a full Spring context, and opaque about their dependencies.&lt;/p&gt;&#xA;&lt;p&gt;The patterns that produce good Spring Boot architectures are not complicated. They require deliberate choices about when to use Spring and when to step back from it.&lt;/p&gt;&#xA;&lt;h2 id=&#34;constructor-injection-is-non-negotiable&#34;&gt;Constructor Injection Is Non-Negotiable&lt;/h2&gt;&#xA;&lt;p&gt;Spring supports three injection mechanisms: constructor, setter, and field injection. Field injection (&lt;code&gt;@Autowired&lt;/code&gt; on a field) is the most common and the worst.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Code Reviews: Reviewing Design, Not Formatting</title>
				<link>https://dplabs.tech/blog/code-reviews-design-not-formatting/</link>
				<pubDate>Mon, 07 Jul 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/code-reviews-design-not-formatting/</guid>
				<description>&lt;p&gt;Code review is one of the highest-leverage practices in software engineering. Done well, it catches defects before they reach production, distributes knowledge, and improves the overall quality of the codebase. Done poorly, it&amp;rsquo;s a bureaucratic checkpoint that blocks delivery while catching only superficial issues.&lt;/p&gt;&#xA;&lt;p&gt;The difference is usually what the reviewer focuses on.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-automated-tools-should-catch&#34;&gt;What Automated Tools Should Catch&lt;/h2&gt;&#xA;&lt;p&gt;The first principle of effective code review: &lt;strong&gt;don&amp;rsquo;t spend human review time on things that tools can detect&lt;/strong&gt;.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Java Performance: Stop Guessing, Start Measuring</title>
				<link>https://dplabs.tech/blog/java-performance-stop-guessing/</link>
				<pubDate>Mon, 23 Jun 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/java-performance-stop-guessing/</guid>
				<description>&lt;p&gt;Most Java performance work starts in the wrong place. An engineer identifies &amp;ldquo;the application is slow,&amp;rdquo; forms a hypothesis based on code familiarity, and makes changes based on intuition. Sometimes it works. More often, the actual bottleneck was elsewhere and the optimized code is now more complex with no performance benefit.&lt;/p&gt;&#xA;&lt;p&gt;The methodology matters: &lt;strong&gt;measure, identify the bottleneck, optimize, measure again&lt;/strong&gt;. In that order. Not &amp;ldquo;optimize the thing that looks suspicious, then measure.&amp;rdquo;&lt;/p&gt;</description>
			</item>
			<item>
				<title>Building Resilient Spring Boot Services</title>
				<link>https://dplabs.tech/blog/spring-boot-resilience/</link>
				<pubDate>Mon, 09 Jun 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/spring-boot-resilience/</guid>
				<description>&lt;p&gt;Resilience patterns exist to handle the failures that are guaranteed to occur in distributed systems. Downstream services become unavailable. Networks time out. Databases become overloaded. These are not exceptional events — they&amp;rsquo;re normal operating conditions that a production service must handle correctly.&lt;/p&gt;&#xA;&lt;p&gt;The mistake is adding resilience patterns everywhere as a precaution. A circuit breaker on a service that never fails adds overhead without value. A retry on a non-idempotent operation can cause duplicate side effects. Resilience patterns should be applied deliberately, to specific failure modes that matter for your system.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Security Is an Architecture Concern</title>
				<link>https://dplabs.tech/blog/security-is-architecture-concern/</link>
				<pubDate>Mon, 26 May 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/security-is-architecture-concern/</guid>
				<description>&lt;p&gt;Security gets treated as a feature set: authentication, authorization, input validation, encryption. Implement those features and the system is secure. This framing is wrong and produces systems that are secure in obvious ways while vulnerable in non-obvious ones.&lt;/p&gt;&#xA;&lt;p&gt;Security is an emergent property of architectural decisions: where trust boundaries are placed, how data flows between components, what permissions components have, how failures are handled. These decisions are made during design, not during a security review at the end.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Deployment Strategies: Blue/Green, Canary, and Rolling Deployments</title>
				<link>https://dplabs.tech/blog/deployment-strategies/</link>
				<pubDate>Mon, 12 May 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/deployment-strategies/</guid>
				<description>&lt;p&gt;A deployment strategy is not just a technical implementation detail. It determines how much risk you take with each release, how quickly you can recover from problems, and what infrastructure you need to support it. Choosing without understanding the trade-offs is choosing accidentally.&lt;/p&gt;&#xA;&lt;h2 id=&#34;rolling-deployments-the-default&#34;&gt;Rolling Deployments: The Default&lt;/h2&gt;&#xA;&lt;p&gt;Rolling deployments incrementally replace old instances with new ones. At any point during the deployment, both old and new versions are running simultaneously.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Structured Concurrency in Java: Fixing Unstructured Concurrent Code</title>
				<link>https://dplabs.tech/blog/structured-concurrency-java/</link>
				<pubDate>Mon, 28 Apr 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/structured-concurrency-java/</guid>
				<description>&lt;p&gt;Java&amp;rsquo;s concurrency model has been functional for decades. &lt;code&gt;ExecutorService&lt;/code&gt;, &lt;code&gt;Future&lt;/code&gt;, &lt;code&gt;CompletableFuture&lt;/code&gt; — these tools work. They also have a persistent problem: when you submit tasks to an executor, the relationship between the spawning thread and the spawned tasks is not represented in the code. Errors are easy to lose. Cancellation doesn&amp;rsquo;t propagate automatically. Resources leak when tasks outlive their intended scope.&lt;/p&gt;&#xA;&lt;p&gt;Structured concurrency, finalized in Java 24 (JEP 505 after multiple preview rounds), fixes this. The core principle: &lt;strong&gt;concurrent tasks should have lifetimes that are scoped to the block of code that spawned them&lt;/strong&gt;, just as &lt;code&gt;try-with-resources&lt;/code&gt; scopes resource lifetimes.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Terraform: Modules, State, and the Hidden Complexity</title>
				<link>https://dplabs.tech/blog/terraform-modules-state/</link>
				<pubDate>Mon, 21 Apr 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/terraform-modules-state/</guid>
				<description>&lt;p&gt;Terraform is the dominant infrastructure as code tool for AWS, GCP, and Azure workloads. Getting started is straightforward. Operating it at scale — across multiple teams, environments, and hundreds of resources — surfaces complexity that the getting-started tutorials don&amp;rsquo;t address.&lt;/p&gt;&#xA;&lt;p&gt;This article focuses on the operational lessons: state management, module architecture, and the patterns that keep Terraform manageable as infrastructure grows.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Terraform is developed by HashiCorp under the BSL license. OpenTofu is the open-source MIT-licensed fork, API-compatible with Terraform 1.5. For teams that require a fully open-source toolchain, OpenTofu is the alternative to evaluate.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Abstraction Is a Cost, Not a Free Feature</title>
				<link>https://dplabs.tech/blog/abstraction-is-a-cost/</link>
				<pubDate>Mon, 07 Apr 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/abstraction-is-a-cost/</guid>
				<description>&lt;p&gt;Software engineering culture values abstraction. Interfaces, layers, generalization, frameworks — these are treated as inherently good. The more abstract your code, the more flexible and reusable it is.&lt;/p&gt;&#xA;&lt;p&gt;This is partially true and partially a way to build systems that are harder to understand than they need to be.&lt;/p&gt;&#xA;&lt;p&gt;Abstraction has real benefits. It also has real costs. The question is whether the benefits outweigh the costs in a specific context — not whether abstraction is good in the abstract.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Spring Boot Observability: Metrics, Tracing, and Logs That Actually Help</title>
				<link>https://dplabs.tech/blog/spring-boot-observability/</link>
				<pubDate>Mon, 31 Mar 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/spring-boot-observability/</guid>
				<description>&lt;p&gt;Spring Boot 3.x has strong observability support built in. Spring Boot Actuator, Micrometer for metrics, Micrometer Tracing (built on OpenTelemetry), and structured logging all work out of the box with minimal configuration.&lt;/p&gt;&#xA;&lt;p&gt;The gap between &amp;ldquo;instrumented&amp;rdquo; and &amp;ldquo;observable&amp;rdquo; is wider than it looks. Adding dependencies and enabling endpoints is instrumentation. Being able to answer &amp;ldquo;why are 3% of requests failing between the payment service and the inventory service, and since when?&amp;rdquo; is observability.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Authentication vs Authorization: Two Problems People Keep Conflating</title>
				<link>https://dplabs.tech/blog/authentication-vs-authorization/</link>
				<pubDate>Mon, 24 Mar 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/authentication-vs-authorization/</guid>
				<description>&lt;p&gt;&amp;ldquo;Authentication&amp;rdquo; and &amp;ldquo;authorization&amp;rdquo; are often used interchangeably. They&amp;rsquo;re not the same problem, they&amp;rsquo;re not the same solution, and conflating them produces security architectures that fail in predictable ways.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-definitions-that-matter&#34;&gt;The Definitions That Matter&lt;/h2&gt;&#xA;&lt;p&gt;&lt;strong&gt;Authentication&lt;/strong&gt;: verifying who someone is.&lt;/p&gt;&#xA;&lt;p&gt;A request is authenticated when the system has verified that the claimed identity (user, service) is legitimate. After authentication, you know &lt;em&gt;who&lt;/em&gt; is making the request.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Authorization&lt;/strong&gt;: determining what someone is allowed to do.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Technical Debt: Not All Debt Is Bad</title>
				<link>https://dplabs.tech/blog/technical-debt-not-all-bad/</link>
				<pubDate>Mon, 03 Mar 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/technical-debt-not-all-bad/</guid>
				<description>&lt;p&gt;Ward Cunningham introduced the technical debt metaphor to explain to non-technical stakeholders why code needed to be refactored. The analogy to financial debt was deliberate: sometimes you borrow money to move faster, and the interest compounds over time until the debt needs to be paid.&lt;/p&gt;&#xA;&lt;p&gt;Over time, the metaphor has been stretched to cover every imperfect thing in a codebase. This is not useful. Not all debt is the same. Not all debt should be paid down.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Spring Boot Testing: What Should Actually Be Tested and How</title>
				<link>https://dplabs.tech/blog/spring-boot-testing-strategy/</link>
				<pubDate>Mon, 10 Feb 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/spring-boot-testing-strategy/</guid>
				<description>&lt;p&gt;Spring Boot&amp;rsquo;s testing support is extensive and well-designed. It&amp;rsquo;s also easy to misuse in ways that produce slow, fragile test suites with poor diagnostic value.&lt;/p&gt;&#xA;&lt;p&gt;The core question is not &amp;ldquo;how do I test with Spring Boot?&amp;rdquo; but &amp;ldquo;what needs to be tested, and at what level?&amp;rdquo;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-testing-pyramid-applied-to-spring-boot&#34;&gt;The Testing Pyramid Applied to Spring Boot&lt;/h2&gt;&#xA;&lt;p&gt;The testing pyramid principle: favor many fast, isolated unit tests over fewer slow integration tests. Each level of the pyramid is slower and more expensive to run but tests more of the system.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Staff Engineers Don&#39;t Just Write Better Code</title>
				<link>https://dplabs.tech/blog/staff-engineers-beyond-code/</link>
				<pubDate>Mon, 03 Feb 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/staff-engineers-beyond-code/</guid>
				<description>&lt;p&gt;Most senior engineers who become staff engineers don&amp;rsquo;t change what they do — they do more of the same, better. They write more code, review more PRs, solve harder technical problems. They&amp;rsquo;re surprised when their promotion doesn&amp;rsquo;t materialize.&lt;/p&gt;&#xA;&lt;p&gt;The confusion is understandable. Senior engineering is primarily about individual technical excellence. Staff engineering is primarily about multiplying the technical effectiveness of the organization around you.&lt;/p&gt;&#xA;&lt;p&gt;These require different skills and, more importantly, a different mental model of what your job is.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Records and Sealed Classes: Better Domain Models in Java</title>
				<link>https://dplabs.tech/blog/records-sealed-classes-domain-models/</link>
				<pubDate>Mon, 27 Jan 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/records-sealed-classes-domain-models/</guid>
				<description>&lt;p&gt;Java domain modeling has historically been verbose and error-prone. Entity classes with dozens of getter/setter pairs, boolean fields that encode state (&lt;code&gt;isActive&lt;/code&gt;, &lt;code&gt;isCancelled&lt;/code&gt;, &lt;code&gt;isPending&lt;/code&gt;), and null fields used to represent the absence of a value all create code that&amp;rsquo;s hard to understand and easy to misuse.&lt;/p&gt;&#xA;&lt;p&gt;Records and sealed classes address this. They&amp;rsquo;re not cosmetic improvements — they change what kinds of mistakes the compiler can catch for you.&lt;/p&gt;&#xA;&lt;h2 id=&#34;records-immutable-data-carriers&#34;&gt;Records: Immutable Data Carriers&lt;/h2&gt;&#xA;&lt;p&gt;A record is a class defined by its components. The compiler generates the constructor, accessors, &lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;hashCode&lt;/code&gt;, and &lt;code&gt;toString&lt;/code&gt; automatically:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Pattern Matching in Modern Java: Records, Switch, and Exhaustiveness</title>
				<link>https://dplabs.tech/blog/pattern-matching-modern-java/</link>
				<pubDate>Mon, 20 Jan 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/pattern-matching-modern-java/</guid>
				<description>&lt;p&gt;Pattern matching in Java 21 is the culmination of several interconnected features: &lt;code&gt;instanceof&lt;/code&gt; patterns (Java 16), sealed classes (Java 17), switch expressions (Java 14), and switch patterns (Java 21). Understanding them individually is useful. Understanding how they compose is where the real power is.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-baseline-pattern-matching-for-instanceof&#34;&gt;The Baseline: Pattern Matching for instanceof&lt;/h2&gt;&#xA;&lt;p&gt;Before Java 16, checking and casting looked like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shape&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;getShape&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shape&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;instanceof&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Circle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Circle&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Circle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shape&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Redundant cast&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;area&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;else&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shape&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;instanceof&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Rectangle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Rectangle&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Rectangle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;shape&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;area&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Java 16 introduced pattern variables in &lt;code&gt;instanceof&lt;/code&gt;:&lt;/p&gt;</description>
			</item>
			<item>
				<title>Clean Code Is Not About Pretty Code</title>
				<link>https://dplabs.tech/blog/clean-code-is-not-about-pretty-code/</link>
				<pubDate>Mon, 13 Jan 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/clean-code-is-not-about-pretty-code/</guid>
				<description>&lt;p&gt;&amp;ldquo;Clean code&amp;rdquo; has become a proxy for &amp;ldquo;code that looks like the author read Clean Code.&amp;rdquo; Short methods named with specific patterns. Classes that follow certain principles. Comments removed because &amp;ldquo;code should be self-documenting.&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;This misses the point. Clean code is not a style. It&amp;rsquo;s code that manages complexity effectively — that can be understood, changed, and extended without surprising consequences.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-complexity-actually-costs&#34;&gt;What Complexity Actually Costs&lt;/h2&gt;&#xA;&lt;p&gt;Every line of code has carrying costs. It needs to be understood before it can be changed. Changed before it can be trusted. Tested before it can be deployed. These costs compound.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Monolith vs Microservices: Starting With the Right Question</title>
				<link>https://dplabs.tech/blog/monolith-vs-microservices/</link>
				<pubDate>Mon, 06 Jan 2025 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/monolith-vs-microservices/</guid>
				<description>&lt;p&gt;The monolith vs microservices debate has produced more heat than light over the past decade. Both sides have oversimplified. &amp;ldquo;Monoliths don&amp;rsquo;t scale&amp;rdquo; is demonstrably false — StackOverflow ran a monolith for years serving massive traffic. &amp;ldquo;Microservices are only for large teams&amp;rdquo; ignores the many small teams that have shipped them successfully.&lt;/p&gt;&#xA;&lt;p&gt;The real question is not which architecture is better in the abstract. It&amp;rsquo;s which architecture fits your current constraints: your team size, your organizational structure, your operational maturity, and your actual scaling requirements.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Event-Driven Architecture: When Events Help and When They Hurt</title>
				<link>https://dplabs.tech/blog/event-driven-architecture/</link>
				<pubDate>Mon, 23 Dec 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/event-driven-architecture/</guid>
				<description>&lt;p&gt;Event-driven architecture has become fashionable, which means it&amp;rsquo;s being applied in situations where it doesn&amp;rsquo;t fit. Events are a powerful architectural tool for specific problems. They&amp;rsquo;re a significant operational burden when applied indiscriminately.&lt;/p&gt;&#xA;&lt;p&gt;Understanding when events help and when they hurt requires understanding what problems they actually solve.&lt;/p&gt;&#xA;&lt;h2 id=&#34;events-vs-commands&#34;&gt;Events vs Commands&lt;/h2&gt;&#xA;&lt;p&gt;The distinction matters more than most tutorials acknowledge.&lt;/p&gt;&#xA;&lt;p&gt;A &lt;strong&gt;command&lt;/strong&gt; expresses intent: &amp;ldquo;place this order,&amp;rdquo; &amp;ldquo;cancel this payment,&amp;rdquo; &amp;ldquo;reserve this inventory item.&amp;rdquo; It&amp;rsquo;s addressed to a specific recipient and implies that recipient is responsible for handling it. Commands can be rejected. They have an expected response.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Java 17 to 21: Modern Java Is a Different Language</title>
				<link>https://dplabs.tech/blog/java-17-to-21-modern-java/</link>
				<pubDate>Mon, 16 Dec 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/java-17-to-21-modern-java/</guid>
				<description>&lt;p&gt;Java 21 is the current long-term support release as of late 2024, and it represents something unusual: a Java that looks substantively different from Java 8. Not just faster or more convenient — different in how you model domains and reason about concurrency.&lt;/p&gt;&#xA;&lt;p&gt;This article focuses on the features that arrived between Java 17 and Java 21 (covered separately) and the cohesive picture they form together.&lt;/p&gt;&#xA;&lt;h2 id=&#34;pattern-matching-for-switch-finalized-java-21&#34;&gt;Pattern Matching for Switch (Finalized Java 21)&lt;/h2&gt;&#xA;&lt;p&gt;Pattern matching for &lt;code&gt;instanceof&lt;/code&gt; arrived in Java 16. Java 21 extended pattern matching to &lt;code&gt;switch&lt;/code&gt; — and this is where the power becomes apparent.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Modern Spring Boot Architecture: Beyond the Three-Layer Application</title>
				<link>https://dplabs.tech/blog/modern-spring-boot-architecture/</link>
				<pubDate>Mon, 02 Dec 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/modern-spring-boot-architecture/</guid>
				<description>&lt;p&gt;The three-layer architecture — controllers, services, repositories — is how most Spring Boot tutorials present application structure. It&amp;rsquo;s a reasonable starting point. It&amp;rsquo;s also a pattern that breaks down as applications grow.&lt;/p&gt;&#xA;&lt;p&gt;The problems are predictable: services become God classes with hundreds of methods, repository calls leak into controllers, domain logic spreads across multiple layers, and changing anything requires understanding the entire application at once. The layered architecture doesn&amp;rsquo;t enforce boundaries — it suggests them. Suggestions decay under deadline pressure.&lt;/p&gt;</description>
			</item>
			<item>
				<title>CI/CD Is a Feedback Loop, Not a Deployment Tool</title>
				<link>https://dplabs.tech/blog/cicd-is-a-feedback-loop/</link>
				<pubDate>Mon, 25 Nov 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/cicd-is-a-feedback-loop/</guid>
				<description>&lt;p&gt;Most CI/CD implementations optimize for the wrong thing. They treat the pipeline as a deployment automation tool — a mechanism for running tests and pushing artifacts to production. The tests pass, the deploy runs, work is done.&lt;/p&gt;&#xA;&lt;p&gt;This framing misses the fundamental value of continuous integration and continuous delivery: &lt;strong&gt;compressing the feedback loop between writing code and understanding its consequences&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The faster that loop runs, the faster defects are caught, the smaller the blast radius of errors, and the more confidence engineers have when deploying. Everything else follows from this.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Architecture Decision Records: Making Technical Decisions Explicit</title>
				<link>https://dplabs.tech/blog/architecture-decision-records/</link>
				<pubDate>Mon, 11 Nov 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/architecture-decision-records/</guid>
				<description>&lt;p&gt;Every significant technical decision your team makes will be questioned later. When the original authors have left. When the context has changed. When someone proposes replacing the system. When a new engineer asks &amp;ldquo;why does this work like this?&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Most teams have no good answer to these questions. The reasons behind decisions live in people&amp;rsquo;s heads, in Slack threads that are impossible to find, or nowhere at all.&lt;/p&gt;&#xA;&lt;p&gt;Architecture Decision Records are a lightweight practice that fixes this. They capture not just what was decided, but why — including what was considered and rejected. The investment is small. The return compounds over time.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Virtual Threads in Java 21: What They Solve and What They Don&#39;t</title>
				<link>https://dplabs.tech/blog/virtual-threads-java-21/</link>
				<pubDate>Mon, 04 Nov 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/virtual-threads-java-21/</guid>
				<description>&lt;p&gt;Virtual threads were the most anticipated Java feature in years. Project Loom&amp;rsquo;s promise was simple: write blocking code that scales like non-blocking code. Java 21 delivered them as a finalized, production-ready feature. They work. The caveats matter.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-problem-virtual-threads-solve&#34;&gt;The Problem Virtual Threads Solve&lt;/h2&gt;&#xA;&lt;p&gt;Traditional Java concurrency has a fundamental constraint: &lt;strong&gt;platform threads are expensive&lt;/strong&gt;. Each platform thread maps 1:1 to an OS thread. OS threads consume roughly 1MB of stack space by default and are expensive to create and context-switch.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Distributed Systems: The Problems You Get for Free</title>
				<link>https://dplabs.tech/blog/distributed-systems-problems/</link>
				<pubDate>Mon, 28 Oct 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/distributed-systems-problems/</guid>
				<description>&lt;p&gt;Every system that communicates over a network is a distributed system, and distributed systems fail in ways that single-process applications don&amp;rsquo;t. This is not opinion — it&amp;rsquo;s physics. Networks drop packets. Clocks drift. Services restart. Disks fail. These things happen regardless of how carefully you write your application code.&lt;/p&gt;&#xA;&lt;p&gt;The teams that get distributed systems right don&amp;rsquo;t eliminate these failures. They design so that their systems remain correct despite them.&lt;/p&gt;</description>
			</item>
			<item>
				<title>RAG Is Not a Database Query</title>
				<link>https://dplabs.tech/blog/rag-is-not-a-database-query/</link>
				<pubDate>Mon, 21 Oct 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/rag-is-not-a-database-query/</guid>
				<description>&lt;p&gt;Retrieval-Augmented Generation has become the default answer to &amp;ldquo;how do we make an LLM answer questions about our data.&amp;rdquo; The concept is simple enough to explain in one slide: embed your documents, store them in a vector database, retrieve the relevant ones at query time, pass them to the LLM.&lt;/p&gt;&#xA;&lt;p&gt;The problem is that the one-slide version produces one-slide-quality results. Good RAG systems are significantly harder to build than the demos suggest, and the failures are often subtle — the system produces confident, plausible-sounding answers that are wrong in ways that are hard to detect without systematic evaluation.&lt;/p&gt;</description>
			</item>
			<item>
				<title>The Modular Monolith: An Architecture Pattern That Deserves More Attention</title>
				<link>https://dplabs.tech/blog/modular-monolith-architecture/</link>
				<pubDate>Mon, 14 Oct 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/modular-monolith-architecture/</guid>
				<description>&lt;p&gt;The software industry has a microservices fixation. Over the last decade, &amp;ldquo;monolith&amp;rdquo; became almost a pejorative, and teams moved to distributed architectures before they had the scale, team size, or operational maturity to justify them.&lt;/p&gt;&#xA;&lt;p&gt;The result has been widespread over-engineering: systems that are harder to understand, harder to debug, harder to change, and more expensive to operate — not because distributed systems are inherently bad, but because the problems they solve weren&amp;rsquo;t actually present.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Java 9 to 17: The Language Features You Should Actually Be Using</title>
				<link>https://dplabs.tech/blog/java-9-to-17-features/</link>
				<pubDate>Mon, 07 Oct 2024 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/blog/java-9-to-17-features/</guid>
				<description>&lt;p&gt;Java 8 was a turning point. Lambdas and streams changed the language substantially. Then came a long stretch where each release felt like maintenance work — modules in 9 were important but painful, and the subsequent releases improved the language incrementally without a clear headline.&lt;/p&gt;&#xA;&lt;p&gt;That changed with Java 14–17. The features that landed in this window — records, sealed classes, pattern matching for &lt;code&gt;instanceof&lt;/code&gt;, text blocks, switch expressions — are not conveniences. They change how you model domains and reason about code.&lt;/p&gt;</description>
			</item>
			<item>
				<title>About</title>
				<link>https://dplabs.tech/about/</link>
				<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/about/</guid>
				<description>&lt;h2 id=&#34;engineering-first-consultancy&#34;&gt;Engineering-first consultancy&lt;/h2&gt;&#xA;&lt;p&gt;dplabs is an independent software engineering and technology consultancy based in Amsterdam, Netherlands.&lt;/p&gt;&#xA;&lt;p&gt;The work focuses on difficult technical problems: systems that need to scale significantly, architectures that have become hard to change, teams that need to make better technical decisions, and organisations that want to adopt AI in a way that actually delivers value.&lt;/p&gt;&#xA;&lt;h2 id=&#34;how-i-work&#34;&gt;How I work&lt;/h2&gt;&#xA;&lt;p&gt;Most consultancies promise transformation. I prefer to focus on specific, tractable problems and solve them well.&lt;/p&gt;</description>
			</item>
			<item>
				<title>AI to Production</title>
				<link>https://dplabs.tech/services/ai-to-production/</link>
				<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/services/ai-to-production/</guid>
				<description>&lt;h2 id=&#34;ai-changed-who-can-build-software-it-didnt-eliminate-the-need-for-software-engineering&#34;&gt;AI changed who can build software. It didn&amp;rsquo;t eliminate the need for software engineering.&lt;/h2&gt;&#xA;&lt;p&gt;Tools like Cursor, Lovable, Bolt, Replit, v0, ChatGPT, and Claude have done something remarkable: they&amp;rsquo;ve made it possible for a founder, product person, or entrepreneur with a clear idea to build a working application — without a team of engineers.&lt;/p&gt;&#xA;&lt;p&gt;That&amp;rsquo;s a genuine shift. What used to take months and significant budget can now take days.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Consultancy</title>
				<link>https://dplabs.tech/services/consultancy/</link>
				<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/services/consultancy/</guid>
				<description>&lt;h2 id=&#34;helping-engineering-teams-build-systems-that-last&#34;&gt;Helping engineering teams build systems that last&lt;/h2&gt;&#xA;&lt;p&gt;Your system works today. The question is whether it&amp;rsquo;ll hold up under 3× the load, after the team doubles, or when that critical service you&amp;rsquo;ve been papering over finally breaks.&lt;/p&gt;&#xA;&lt;p&gt;I work with engineering teams on the problems that matter most: systems that are hard to change, architectures that don&amp;rsquo;t scale, reliability issues that are eating engineering time, and technical debt that&amp;rsquo;s slowing everything down.&lt;/p&gt;</description>
			</item>
			<item>
				<title>Contact</title>
				<link>https://dplabs.tech/contact/</link>
				<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/contact/</guid>
				<description></description>
			</item>
			<item>
				<title>Fractional CTO</title>
				<link>https://dplabs.tech/services/fractional-cto/</link>
				<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
				<guid>https://dplabs.tech/services/fractional-cto/</guid>
				<description>&lt;h2 id=&#34;senior-technical-leadership-without-the-full-time-cost&#34;&gt;Senior technical leadership, without the full-time cost&lt;/h2&gt;&#xA;&lt;p&gt;You&amp;rsquo;ve built an engineering team. They&amp;rsquo;re capable. But somewhere along the way, the technical decisions have started to pile up, the architecture has drifted, and the team is spending more time fighting fires than building product. Or perhaps you&amp;rsquo;re scaling fast and the team needs someone who&amp;rsquo;s done this before.&lt;/p&gt;&#xA;&lt;p&gt;A fractional CTO gives you experienced technical leadership — the kind that typically costs €200K+ per year as a full-time hire — on a part-time basis that fits your current stage.&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
