<?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: Chris Davies</title>
    <description>The latest articles on DEV Community by Chris Davies (@chris_davies).</description>
    <link>https://dev.to/chris_davies</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3282125%2F9da84312-bac3-402d-a83e-ec02c9de2db8.jpg</url>
      <title>DEV Community: Chris Davies</title>
      <link>https://dev.to/chris_davies</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chris_davies"/>
    <language>en</language>
    <item>
      <title>You build it, you budget it? 💰</title>
      <dc:creator>Chris Davies</dc:creator>
      <pubDate>Wed, 06 Aug 2025 15:05:00 +0000</pubDate>
      <link>https://dev.to/chris_davies/you-build-it-you-budget-it-3a51</link>
      <guid>https://dev.to/chris_davies/you-build-it-you-budget-it-3a51</guid>
      <description>&lt;p&gt;📝 &lt;strong&gt;This is cross-posted from my personal site - &lt;a href="https://chrisdavies.dev" rel="noopener noreferrer"&gt;https://chrisdavies.dev&lt;/a&gt; .&lt;/strong&gt;  &lt;/p&gt;




&lt;p&gt;Figma's recent S-1 filing was called out in a &lt;a href="https://www.infoq.com/news/2025/07/figma-aws-300k-daily-bill/" rel="noopener noreferrer"&gt;flurry&lt;/a&gt; &lt;a href="https://www.datacenterdynamics.com/en/news/design-platform-figma-spends-300000-on-aws-daily/" rel="noopener noreferrer"&gt;of&lt;/a&gt; &lt;a href="https://news.ycombinator.com/item?id=44462016" rel="noopener noreferrer"&gt;headlines&lt;/a&gt; for declaring a spend of &lt;em&gt;"$300,000 a day"&lt;/em&gt; on AWS. At face value, that number seems shocking, but perhaps the industry &lt;em&gt;reaction itself&lt;/em&gt; is the main thing worth examining. &lt;/p&gt;

&lt;p&gt;Many organizations subscribe to the "&lt;em&gt;&lt;a href="https://www.thoughtworks.com/en-gb/insights/decoder/y/you-build-it-you-run-it" rel="noopener noreferrer"&gt;you build it, you &lt;strong&gt;run&lt;/strong&gt; it&lt;/a&gt;&lt;/em&gt;" operating model, but this ownership &lt;em&gt;rarely&lt;/em&gt; extends to costs. When was the last time your pager went off in the night because a service experienced an unusual spike in costs? ☎️🧑‍🚒&lt;/p&gt;

&lt;p&gt;In this post, we look at:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Why costs are a &lt;strong&gt;shared responsibility&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;What developers (and tooling providers) can do to help own them &lt;strong&gt;early on&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The vendor "lock-in" &lt;strong&gt;trade-off&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This post was largely inspired by SigNoz's post: &lt;a href="https://signoz.io/blog/why-observability-isnt-just-for-sres/" rel="noopener noreferrer"&gt;"Observability isn't just for SREs"&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  FinOps 💰
&lt;/h3&gt;

&lt;p&gt;Cost management can often feel far-removed from an engineer's remit. The reality is, behind staffing costs, hosting costs are one of the biggest line items for a business, and &lt;a href="https://www.finops.org/" rel="noopener noreferrer"&gt;FinOps&lt;/a&gt; is one of the few engineering disciplines where you can know your impact on the bottom line within 24 hours.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83juu19cq4ejb8bfhtol.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F83juu19cq4ejb8bfhtol.gif" alt="Shovelling money into the Furnace"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Companies like &lt;a href="https://basecamp.com/cloud-exit" rel="noopener noreferrer"&gt;37signals&lt;/a&gt; have been vocal about their cloud exit, presumably feeling like this when it came to paying the invoice.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  "Frivolous" Figma 🤑
&lt;/h4&gt;

&lt;p&gt;Figma's reported daily spend immediately becomes a moot point when you hear that they are running with a 91% gross margin and their infrastructure spend is only 12% of their revenue. They have been public about the &lt;a href="https://www.figma.com/blog/the-infrastructure-behind-ai-search-in-figma/" rel="noopener noreferrer"&gt;many&lt;/a&gt; &lt;a href="https://www.figma.com/blog/how-figmas-databases-team-lived-to-tell-the-scale/" rel="noopener noreferrer"&gt;ways&lt;/a&gt; they have reduced cloud spend.&lt;br&gt;
Thankfully, a lot of the noise was debunked by prominent voices such as &lt;a href="https://www.duckbillgroup.com/blog/figmas-300k-daily-aws-bill-isnt-the-scandal-you-think-it-is/" rel="noopener noreferrer"&gt;Corey Quinn&lt;/a&gt;. The reaction is interesting though - do engineers &lt;em&gt;really know&lt;/em&gt; what a reasonable hosting cost is at their organization, or what &lt;em&gt;gross-margin&lt;/em&gt; is?&lt;/p&gt;
&lt;h3&gt;
  
  
  Availability often trumps Costs (🧑‍🚒 &amp;gt; 💸)
&lt;/h3&gt;

&lt;p&gt;It is understandable why many organizations implicitly prioritise availability over costs in their definition of "owning" a production system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Visibility&lt;/strong&gt;: The former is immediately visible to clients, so does not carry as much immediate reputational damage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: It is easier to reason about. An SLA is a static target. Costs are dynamic - each part of the system's spend may correlate with different factors (e.g. scheduled bursts or spikes due to system activity).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maturity&lt;/strong&gt;: Startups desperately need market fit before they can set up a rigid FinOps strategy. If growth is sufficiently high in the early stages, costs are less of a concern. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: The ironic one - engineers are expensive, so having them purely focus on this is sometimes not viable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Despite feeling like orthogonal concerns, several concepts we use to describe availability could also carry over into the FinOps space.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Availability 🧑‍🚒&lt;/th&gt;
&lt;th&gt;FinOps 💸&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SLA&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Target spend - a fixed $ value, or % of revenue.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alert&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A budget breach or anomaly alarm.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MTTD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mean time (duration) to &lt;em&gt;detect&lt;/em&gt; a cost spike.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MTTR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mean time (duration) to &lt;em&gt;recover&lt;/em&gt; from a cost spike.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Incident&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An investigation into an unexplained cost.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Observability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cost and usage reports/dashboards.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Conway's Law: A limiting factor? ⚖️
&lt;/h3&gt;

&lt;p&gt;Many organizations choose to divide teams into Product and Platform responsibilities as a way of creating abstraction. As &lt;a href="https://en.wikipedia.org/wiki/Conway%27s_law" rel="noopener noreferrer"&gt;Conway's Law&lt;/a&gt; describes, a system architecture will become a representation of the organization structure.  This results in situations where developers shipping features often do not understand the infrastructure that their code runs on. Greater abstraction means freedom at the infrastructure level, but also means less scrutiny on hosting costs for developers who aren't operating at that level of the stack.&lt;/p&gt;

&lt;p&gt;The key trade-off in these team boundaries, is that many meaningful cost optimization opportunities arise in the &lt;strong&gt;intersection&lt;/strong&gt; of what Product and Platform Engineering teams can achieve alone. If the responsibility to optimise costs lies with a central team, the goal becomes a moving target as micro-level decisions elsewhere slowly negate the impact of planned work. Efforts significantly reduce if a culture of awareness/ownership exists across teams. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuee8gvt5ov7ne3lg1l6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuee8gvt5ov7ne3lg1l6.png" alt="The Platform-Product Venn diagram"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A simplified model - responsibilities will vary by org, with impactful efforts sitting in the overlap.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Optimisations require partnership 🤝
&lt;/h3&gt;

&lt;p&gt;In a series of projects spanning a few quarters, we managed to reduce our AWS spend by 40%. This was no mean feat, but a key lesson was that &lt;em&gt;every optimisation&lt;/em&gt; required communication between Platform and Product teams. Whether gathering data, getting buy-in or keeping teams in the loop in case of unexpected outages.&lt;/p&gt;

&lt;p&gt;Life is also made significantly easier if Product-focused teams can readily help explain &lt;em&gt;ongoing cost changes&lt;/em&gt; (e.g., a new client onboard, a recent feature rollout etc.). If revenue increases proportionally, it becomes less of a concern and budgets can be adjusted to the new baseline.&lt;/p&gt;

&lt;p&gt;Let's look at some common types of optimisation, and the level of their cross-team dependencies. These are placed in order of &lt;strong&gt;increasing dependency&lt;/strong&gt; on Product context, to squeeze the most value out of each optimisation.&lt;/p&gt;
&lt;h4&gt;
  
  
  Settings ⚙️
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Settings which are low-effort to change, and should arguably be defaults. Once enabled, they are immediately effective.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;e.g.&lt;/strong&gt;: Using &lt;a href="https://aws.amazon.com/s3/storage-classes/intelligent-tiering/" rel="noopener noreferrer"&gt;intelligent tiering&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucket-key.html" rel="noopener noreferrer"&gt;bucket encryption keys&lt;/a&gt;, different architectures (e.g. &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/optimize-costs-microsoft-workloads/net-graviton.html" rel="noopener noreferrer"&gt;Graviton&lt;/a&gt;), log &lt;a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CloudWatch_Logs_Log_Classes.html" rel="noopener noreferrer"&gt;classes&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;: Co-ordination is needed during testing and rollout to minimise impact on production environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Cleanup 🧹
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Reducing the footprint of compute services and managing the lifetime of data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;e.g.&lt;/strong&gt;: Removing unused services, adding &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html" rel="noopener noreferrer"&gt;lifecycle policies&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html" rel="noopener noreferrer"&gt;TTLs&lt;/a&gt;, &lt;a href="https://docs.aws.amazon.com/batch/latest/userguide/job_timeouts.html" rel="noopener noreferrer"&gt;job timeouts&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;: Much like &lt;a href="https://www.hyrumslaw.com/" rel="noopener noreferrer"&gt;Hyrum's law&lt;/a&gt; for APIs, it is almost never clear which service or data is relied upon until it is deleted. Product teams can provide context on which service/data is valuable and for which initiatives/clients far quicker than metrics would alone. Metrics tell you the current state of the world - not what to expect in the near future.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Right-sizing 🤏
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Matching the reserved capacity of compute services as closely to their usage as possible.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;e.g.&lt;/strong&gt;: Changing CPU and memory (statically or dynamically), removing redundant instances, adopting serverless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;: If embracing serverless, there are fundamental limitations (e.g. memory, timeouts) that Product teams need to know about when shipping code. Right-sizing might mean use of horizontal or vertical scaling, which means choosing a sensible scaling strategy (e.g. scheduled, on-demand). This all requires crucial Product context.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Re-architecting 🎲
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Changing the operating model or system architecture of a service, accepting a reasonable trade-off (e.g. performance, availability) for a significant cost reduction. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;e.g.&lt;/strong&gt;: Embracing event driven architecture (&lt;a href="https://aws.amazon.com/what-is/eda/" rel="noopener noreferrer"&gt;EDA&lt;/a&gt;), removing middleware (e.g. queues/load balancers/proxies), using &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html" rel="noopener noreferrer"&gt;Spot compute&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;: Adopting EDA might add integration latency at key points in a customer workflow. Using interruptible batch jobs could delay critical outputs if there is no checkpointing in use when the job restarts. Metrics can help guide decisions on how often middleware services add value, but Product context is needed to assess whether they are needed long-term. What might seem overkill for today's scale might be perfectly sensible for an upcoming initiative.
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Code changes 🧩
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Modifying the behaviour of application code to alter its usage of resources (e.g., databases, storage, APIs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;e.g.&lt;/strong&gt;: Caching, "pushing down" data filtering, changing storage/API access patterns, reducing logging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;: Often the hardest optimisations for Platform teams to spot if relying on infrastructure metrics alone. An isolated measure of CPU/memory usage or database accesses will not tell you whether there is &lt;em&gt;room to meaningfully optimise behaviour&lt;/em&gt;. This is where an observability stack of Logs, Metrics and Traces matters. Everyday code changes can introduce large changes in the cost-profile of a service. &lt;em&gt;Shifting left&lt;/em&gt; is even more important at this level (discussed below).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Shifting left ⏪
&lt;/h3&gt;

&lt;p&gt;So far, we have only touched on &lt;em&gt;optimisations&lt;/em&gt; once costs are already a problem. With greater ownership across teams, we can shift a lot of these concerns "left" earlier in the development cycle, to reduce the size of future problems. Not every developer needs to take on large, multi-week optimisation projects to be seen as "owning" costs. &lt;/p&gt;

&lt;p&gt;A sad irony is that getting visibility into costs itself carries a cost (through additional services and SaaS subscriptions), but the payoff is often worthwhile.&lt;/p&gt;
&lt;h4&gt;
  
  
  Observability 📊
&lt;/h4&gt;

&lt;p&gt;Giving teams &lt;em&gt;visibility&lt;/em&gt; of their baseline costs is the first step.  If using a cloud provider, a lot of these primitives are provided:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tagging&lt;/strong&gt;: Label infrastructure by team/project. Regularly monitor Cost Explorer dashboards (e.g. &lt;a href="https://aws.amazon.com/aws-cost-management/aws-cost-explorer/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;/&lt;a href="https://cloud.google.com/stackdriver/docs/costs/optimize-costs" rel="noopener noreferrer"&gt;GCP&lt;/a&gt;/&lt;a href="https://www.apptio.com/products/kubecost/" rel="noopener noreferrer"&gt;KubeCost&lt;/a&gt;) and schedule reports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alerting&lt;/strong&gt;: Set up budgets and anomaly detection, with integrations to the communication platforms (e.g. Slack &amp;amp; incident.io).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt;: Monitor usage metrics for each third-party provider / service / API that has its own pricing model.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once observability is in place, let's look at some practical methods that enable cost ownership earlier on in development. &lt;/p&gt;
&lt;h4&gt;
  
  
  Designs 🎨
&lt;/h4&gt;

&lt;p&gt;During the design of a new service or feature, ADRs (Architectural Decision Records) could explicitly consider the cost impact of decisions - forcing early discussion. Whilst it is tricky to provide exact dollar estimates, a "back of the envelope" calculation is made far easier by cloud providers' &lt;a href="https://calculator.aws/#/" rel="noopener noreferrer"&gt;Pricing Calculators&lt;/a&gt;. In the age of AI and LLMs with &lt;a href="https://ethanding.substack.com/p/ai-subscriptions-get-short-squeezed" rel="noopener noreferrer"&gt;spiralling costs there&lt;/a&gt;, there are even &lt;a href="https://pricepertoken.com/" rel="noopener noreferrer"&gt;token calculators&lt;/a&gt;!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A mental model I like to use for Cloud costs is:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Compute = (Capacity * Time)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Storage = (Retention * Data Size) + Data Transferred&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Logging = (Retention * Event Count * Event Size)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Tooling 🛠️
&lt;/h4&gt;

&lt;p&gt;A range of tooling exists to target the IDE and Pull Request workflows, which developers can use to estimate and reduce cost impact before code is shipped.&lt;/p&gt;

&lt;p&gt;For infrastructure changes, tools like &lt;a href="https://www.infracost.io/" rel="noopener noreferrer"&gt;Infracost&lt;/a&gt; offer comparisons in the IDE and PR reviews. At the application layer, I've had some success using &lt;a href="https://docs.datadoghq.com/developers/ide_plugins/idea/" rel="noopener noreferrer"&gt;Datadog's IDEA plugin&lt;/a&gt; to remove noisy logs and optimise frequently executed code. There is room for vendors to go further in this space, and bring cost awareness to the IDE for application code. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwftzvc9m5c69lz126b8a.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwftzvc9m5c69lz126b8a.webp" alt="Viewing costs inside a pull request diff"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;em&gt;Image credit: &lt;a href="https://www.infracost.io/products/" rel="noopener noreferrer"&gt;Infracost&lt;/a&gt;&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Artificial Intelligence 🤖
&lt;/h4&gt;

&lt;p&gt;As AI tooling like &lt;a href="https://www.anthropic.com/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; and &lt;a href="https://www.coderabbit.ai/" rel="noopener noreferrer"&gt;CodeRabbit&lt;/a&gt; start to integrate into CLI/IDE and VCS software, there is a natural space to ensure costs are considered with every prompt. &lt;/p&gt;

&lt;p&gt;Amazon's &lt;a href="https://kiro.dev/" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; is an interesting proposition here. Providing "spec" documents and breaking tasks down like a human before code is generated would naturally lend itself to feeding in ADRs like those described above.  If a file exists in the repository describing the system's tenancy model, budget and priorities as a system prompt, then the code these tools generate could reflect that. &lt;/p&gt;
&lt;h3&gt;
  
  
  "Lock-in": Friend or foe? 🔐
&lt;/h3&gt;

&lt;p&gt;The final topic we will touch on here is vendor &lt;em&gt;"lock-in"&lt;/em&gt;. A lot of the scrutiny on Figma's IPO filings came from their $545m-per-year &lt;em&gt;multi-year&lt;/em&gt; commitment to AWS. By admitting their reliance on a sole provider and solidifying plans to spend across a long-term horizon, it was seen by many as a big &lt;em&gt;concentration risk&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;As &lt;a href="https://www.duckbillgroup.com/blog/figmas-300k-daily-aws-bill-isnt-the-scandal-you-think-it-is/" rel="noopener noreferrer"&gt;Corey Quinn&lt;/a&gt; points out, that size of commitment may be risky for a company hosting "lifted and shifted" VMs where compute costs come at a premium, but Figma's technical stack is deeply ingrained in AWS ecosystem already. Once you are far enough along the adoption curve of a particular cloud provider, it leaves a lot of money on the table if you &lt;strong&gt;do not&lt;/strong&gt; make commitments. This could be explicit commitments via Savings Plans/reserved capacity, or implicit ones like moving &lt;a href="https://serverlessland.com/content/guides/refactoring-serverless/introduction" rel="noopener noreferrer"&gt;"glue code" to the infrastructure layer&lt;/a&gt;. The latter can save many Engineering hours, in return for greater dependence on a specific provider's primitives.&lt;/p&gt;
&lt;h4&gt;
  
  
  Serverless Refactoring ☁️
&lt;/h4&gt;

&lt;p&gt;One of my go-to talks from recent AWS re:Invent conferences is about &lt;em&gt;Serverless Refactoring&lt;/em&gt;, which blurs the line between application and infrastructure concerns.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;TL;DW (Too Long; Didn't Watch)&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;The next time you find yourself writing code to integrate two pieces of infrastructure, it's worth questioning if a different primitive exists instead.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/bIu8XZZROw4"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary 🧵
&lt;/h3&gt;

&lt;p&gt;This post tried to cover a lot of ground. Here are my main takeaways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;💸 Costs are a &lt;strong&gt;shared responsibility&lt;/strong&gt; across development teams, like availability, with a more immediate ROI. Delegating responsibility to a single platform/FinOps team leaves many optimisations off the table, and may elongate projects.&lt;/li&gt;
&lt;li&gt;💻 Developer tooling and AI can &lt;strong&gt;shift a lot of effort left&lt;/strong&gt;. Producing code will become less of bottleneck as more of this responsibility is delegated to machines. However, understanding how that code impacts system reliability and costs will only matter more over time. &lt;/li&gt;
&lt;li&gt;🔐 &lt;em&gt;"Lock-in"&lt;/em&gt; and cloud commitments get a bad reputation, because it's harder to quantify the &lt;strong&gt;opportunity cost&lt;/strong&gt; of not going "all-in" on providers' offerings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As always, context matters, so the next time we see a headline about an organization's hosting costs, it's worthwhile treating it with the nuance it deserves, rather than being outraged by the headlines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get in touch 📧
&lt;/h3&gt;

&lt;p&gt;As I say in my &lt;a href="//../../about/"&gt;About&lt;/a&gt; page, I would love to hear from you. If you got to the end of this post and have anything to share, please get in touch on &lt;a href="https://www.linkedin.com/in/c-j-davies/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://x.com/c_davies21" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In future posts, I may dive into some neat cost management measures I have seen in the wild. In the meantime, here are a few podcast recommendations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎧 &lt;a href="https://www.lastweekinaws.com/podcast/" rel="noopener noreferrer"&gt;Screaming in the Cloud (Last Week in AWS)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🎧 &lt;a href="https://www.doit.com/podcasts/cloud-masters/" rel="noopener noreferrer"&gt;Cloud Masters (DoIt)&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>finops</category>
      <category>cloudcomputing</category>
      <category>aws</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Just Use Postgres 🐘</title>
      <dc:creator>Chris Davies</dc:creator>
      <pubDate>Sun, 06 Jul 2025 07:37:06 +0000</pubDate>
      <link>https://dev.to/chris_davies/just-use-postgres-6li</link>
      <guid>https://dev.to/chris_davies/just-use-postgres-6li</guid>
      <description>&lt;p&gt;📝 &lt;strong&gt;This is cross-posted from my personal site - &lt;a href="https://chrisdavies.dev" rel="noopener noreferrer"&gt;https://chrisdavies.dev&lt;/a&gt; .&lt;/strong&gt;  &lt;/p&gt;




&lt;p&gt;A common adage for any developer choosing a database technology is &lt;em&gt;"just use Postgres"&lt;/em&gt;. While this advice remains well-intentioned, it deserves a fresh look in 2025. Thanks to a wave of recent innovations and acquisitions, PostgreSQL providers now resemble JavaScript frameworks of the 2010s - there’s seemingly a new one to consider every six months.&lt;/p&gt;

&lt;p&gt;Just choosing PostgreSQL isn’t enough anymore - there is an &lt;em&gt;entirely new decision tree&lt;/em&gt; to follow before you can launch a system. 🌳&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: The answer to &lt;em&gt;"Which one do I choose?"&lt;/em&gt; is always &lt;em&gt;"It depends"&lt;/em&gt;, but this post signposts exactly what those deciding factors are. ⚖️ &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6a6md0gipjmbyi020fb3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6a6md0gipjmbyi020fb3.jpg" alt="Thailand elephant sanctuary"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Postgres uses an elephant as its mascot. This means I have a good excuse to reminisce on my March 2025 trip to Thailand&lt;/em&gt; 🇹🇭&lt;/p&gt;
&lt;h3&gt;
  
  
  Postgres Renaissance 🎨
&lt;/h3&gt;

&lt;p&gt;The saying &lt;a href="https://mccue.dev/pages/8-16-24-just-use-postgres" rel="noopener noreferrer"&gt;&lt;em&gt;"just use Postgres"&lt;/em&gt;&lt;/a&gt; is deliberately simplistic. It is intended to reduce analysis paralysis at a critical point in a system's lifetime. Postgres is the "boring" choice because it has been battle-tested in production for decades by thousands of companies. Being &lt;a href="https://github.com/postgres/postgres" rel="noopener noreferrer"&gt;open-source&lt;/a&gt; and &lt;a href="https://medium.com/agedb/introduction-to-postgresql-extensions-770e37f3fae1" rel="noopener noreferrer"&gt;extensible&lt;/a&gt;, it has a &lt;a href="https://www.postgresql.org/community/" rel="noopener noreferrer"&gt;thriving community&lt;/a&gt;. There is very little to gain by choosing a less-proven technology. Or one with restrictive licensing. Or adopting several solutions with their own cost models. It closes the fewest number of doors when a project's uncertainty is at its highest. &lt;/p&gt;

&lt;p&gt;Recent Postgres versions and extensions (e.g &lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;&lt;code&gt;pgvector&lt;/code&gt;&lt;/a&gt;) have introduced and significantly improved support for vector/JSON data, which makes it even harder to justify starting with a specialised store. Extensions like &lt;a href="https://github.com/duckdb/pg_duckdb" rel="noopener noreferrer"&gt;&lt;code&gt;pg_duckdb&lt;/code&gt;&lt;/a&gt; support column-oriented, analytical workloads inside a row-oriented, transactional system. &lt;a href="https://www.tigerdata.com/blog/how-to-collapse-your-stack-using-postgresql-for-everything" rel="noopener noreferrer"&gt;Many&lt;/a&gt; &lt;a href="https://dev.to/shayy/postgres-is-too-good-and-why-thats-actually-a-problem-4imc"&gt;articles&lt;/a&gt; exist on the versatility of Postgres. Some even go as far as stating you can host a full REST API with &lt;a href="https://docs.postgrest.org/en/v13/" rel="noopener noreferrer"&gt;PostgREST&lt;/a&gt; (though I haven't seen Production examples of this).&lt;/p&gt;

&lt;p&gt;In recent times, database technologies derived from Postgres have exploded in number and seen rapid success. In the last few months alone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🤑 &lt;a href="https://www.databricks.com/company/newsroom/press-releases/databricks-agrees-acquire-neon-help-developers-deliver-ai-systems" rel="noopener noreferrer"&gt;Databricks acquired Neon&lt;/a&gt;, a serverless Postgres provider.&lt;/li&gt;
&lt;li&gt;🤑 &lt;a href="https://www.crunchydata.com/blog/crunchy-data-joins-snowflake" rel="noopener noreferrer"&gt;Snowflake acquired Crunchy Data&lt;/a&gt;, a hosted Postgres provider.&lt;/li&gt;
&lt;li&gt;🎉 &lt;a href="https://aws.amazon.com/blogs/aws/amazon-aurora-dsql-is-now-generally-available/" rel="noopener noreferrer"&gt;AWS Aurora DSQL&lt;/a&gt; was released.&lt;/li&gt;
&lt;li&gt;🎉 PlanetScale expanded on its MySQL foundation, to offer a &lt;a href="https://planetscale.com/blog/planetscale-for-postgres" rel="noopener noreferrer"&gt;Postgres alternative&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is a &lt;em&gt;non-exhaustive&lt;/em&gt; list of recent technologies derived from Postgres, and their offerings.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://neon.com/" rel="noopener noreferrer"&gt;Neon&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless, fully-managed instances with autoscaling and branching.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://www.thenile.dev/" rel="noopener noreferrer"&gt;Nile&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless, fully-managed instances for multi-tenant applications (e.g. B2B SaaS).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://supabase.com/database" rel="noopener noreferrer"&gt;Supabase&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open-source Firebase alternative - either managed or self-hosted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://www.tigerdata.com/" rel="noopener noreferrer"&gt;TigerData (TimescaleDB)&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optimized for time-series and real-time analytics - either managed or self-hosted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://www.cockroachlabs.com/" rel="noopener noreferrer"&gt;CockroachDB&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distributed SQL with Postgres compatibility - either managed or self-hosted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://planetscale.com/blog/planetscale-for-postgres" rel="noopener noreferrer"&gt;PlanetScale Postgres&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed instances, emphasizing performance and scalability based on &lt;a href="https://planetscale.com/blog/announcing-metal" rel="noopener noreferrer"&gt;NVMe SSDs&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://www.prisma.io/postgres" rel="noopener noreferrer"&gt;Prisma&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Serverless, fully-managed instances with a TypeScript &lt;a href="https://www.prisma.io/docs" rel="noopener noreferrer"&gt;Object Relational Mapper (ORM)&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://www.yugabyte.com/" rel="noopener noreferrer"&gt;YugabyteDB&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distributed SQL with Postgres compatibility - either managed or self-hosted. &lt;a href="https://www.yugabyte.com/blog/postgresql-upgrade-framework/" rel="noopener noreferrer"&gt;Zero-downtime&lt;/a&gt; upgrades.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://www.crunchydata.com/" rel="noopener noreferrer"&gt;Crunchy Data&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fully-compatible Postgres instances for Enterprise - either managed or self-hosted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://www.heroku.com/postgres/" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed instances integrated into the Heroku platform (PaaS).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS &lt;a href="https://aws.amazon.com/rds/" rel="noopener noreferrer"&gt;RDS&lt;/a&gt; / &lt;a href="https://aws.amazon.com/rds/aurora/" rel="noopener noreferrer"&gt;Aurora&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed instances integrated into the AWS platform - provisioned or serverless.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google &lt;a href="https://cloud.google.com/sql" rel="noopener noreferrer"&gt;Cloud SQL&lt;/a&gt; / &lt;a href="https://cloud.google.com/products/alloydb" rel="noopener noreferrer"&gt;AlloyDB&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed instances integrated into the Google Cloud platform - provisioned or serverless.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;a href="https://azure.microsoft.com/en-us/products/postgresql" rel="noopener noreferrer"&gt;Azure Database&lt;/a&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Managed instances integrated into the Azure platform - provisioned or serverless.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kpozed83leqhdc4626u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2kpozed83leqhdc4626u.png" alt="Using Postgres for everything"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Image credit: &lt;a href="https://www.tigerdata.com/blog/how-to-collapse-your-stack-using-postgresql-for-everything" rel="noopener noreferrer"&gt;TigerData&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Cautionary Tales ⚠️
&lt;/h3&gt;

&lt;p&gt;Choosing Postgres over other relational databases doesn't always go to plan. Uber famously &lt;a href="https://www.uber.com/en-GB/blog/postgres-to-mysql-migration/" rel="noopener noreferrer"&gt;switched&lt;/a&gt; to MySQL after facing slowness during writes, favouring the way MySQL handles index updates. Other systems have &lt;a href="https://www.cs.cmu.edu/~pavlo/blog/2023/04/the-part-of-postgresql-we-hate-the-most.html" rel="noopener noreferrer"&gt;faced issues&lt;/a&gt; such as table bloat and transaction ID wraparound due to Postgres’s MVCC model, which avoids locks in favor of versioning.&lt;/p&gt;

&lt;p&gt;This is a good opportunity to preach the lessons from &lt;a href="https://www.oreilly.com/library/view/designing-data-intensive-applications/9781491903063/" rel="noopener noreferrer"&gt;&lt;em&gt;Designing Data Intensive Applications&lt;/em&gt;&lt;/a&gt;. Without understanding the underlying storage and retrieval technology these databases rely on, you are likely to face surprises in Production.&lt;/p&gt;
&lt;h3&gt;
  
  
  Recent Trends 🔨
&lt;/h3&gt;

&lt;p&gt;In this section, we go over the main innovations that have emerged in recent years, which have caused the explosion in providers, each with their own unique offerings. As Postgres is open-source, it is possible to cherry-pick features and compromise on some fundamental aspects in order to unlock new behaviours.&lt;/p&gt;
&lt;h4&gt;
  
  
  Compute &amp;lt;&amp;gt; Storage Divide ➗
&lt;/h4&gt;

&lt;p&gt;Postgres pre-dates the age of cloud and distributed systems. As such, it is monolithic (combining storage and compute on the same server) and process-oriented (rather than &lt;a href="https://engineeringatscale.substack.com/p/will-postgresql-switch-to-a-thread" rel="noopener noreferrer"&gt;thread-oriented&lt;/a&gt;). To thrive in a cloud-native world, providers have &lt;a href="https://thenewstack.io/new-oltp-postgres-with-separate-compute-and-storage/" rel="noopener noreferrer"&gt;decoupled&lt;/a&gt; storage and compute layers, which allows each layer to scale independently. This change enables &lt;a href="https://jack-vanlightly.com/analyses/2023/11/15/neon-serverless-postgresql-asds-chapter-3" rel="noopener noreferrer"&gt;serverless&lt;/a&gt; systems like Neon and AWS Aurora Serverless. Use of durable block storage like S3 means lightweight VMs can run the Query Engine and hold minimal state, allowing horizontal scalability. Different systems embrace this to different extents. For example, Aurora Serverless v2 incurs a 15 second cold-start when &lt;a href="https://aws.amazon.com/blogs/database/introducing-scaling-to-0-capacity-with-amazon-aurora-serverless-v2/" rel="noopener noreferrer"&gt;scaling up from zero&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Besides scalability, the other by-products of standalone storage are &lt;a href="https://neon.com/docs/introduction/branching" rel="noopener noreferrer"&gt;database branching&lt;/a&gt; and &lt;a href="https://aws.amazon.com/blogs/aws/amazon-aurora-fast-database-cloning/" rel="noopener noreferrer"&gt;fast backups&lt;/a&gt; due to copy-on-write semantics. These are game-changing features that a traditional Postgres system cannot provide. &lt;/p&gt;
&lt;h4&gt;
  
  
  Relaxing Isolation for Speed 🏎️
&lt;/h4&gt;

&lt;p&gt;Postgres is flexible in supporting 4 levels of &lt;a href="https://www.postgresql.org/docs/current/transaction-iso.html" rel="noopener noreferrer"&gt;transaction isolation&lt;/a&gt; at session or transaction level. By constraining the isolation level to &lt;code&gt;Repeatable Read&lt;/code&gt; (using something like Snapshot Isolation) rather than &lt;code&gt;Serializable&lt;/code&gt;, we require far less consensus before transactions can commit or return data.     &lt;/p&gt;

&lt;p&gt;In most applications, the key-set of data being read is far greater than the key-set of data being written, and reads outnumber writes by an order of magnitude. This realisation along with constraining isolation level to &lt;code&gt;Repeatable Read&lt;/code&gt; is core to the &lt;a href="https://brooker.co.za/blog/2024/12/17/occ-and-isolation.html" rel="noopener noreferrer"&gt;way AWS DSQL scales&lt;/a&gt;. By constraining in one dimension, new technologies are unlocking scale that a traditional setup could not match.&lt;/p&gt;
&lt;h4&gt;
  
  
  New Data Primitives 📊️
&lt;/h4&gt;

&lt;p&gt;Being a general purpose and extensible database, some providers have imposed an opinionated abstraction on top of core Postgres. For example, TigerData introduces &lt;a href="https://docs.tigerdata.com/use-timescale/latest/hypertables/" rel="noopener noreferrer"&gt;&lt;code&gt;hypertables&lt;/code&gt;&lt;/a&gt; to model time-series data, which would otherwise need manual partitioning and regular pruning via extensions like &lt;a href="https://github.com/pgpartman/pg_partman" rel="noopener noreferrer"&gt;&lt;code&gt;pg_partman&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/citusdata/pg_cron" rel="noopener noreferrer"&gt;&lt;code&gt;pg_cron&lt;/code&gt;&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;AI companies make use of vector stores (through &lt;a href="https://github.com/pgvector/pgvector" rel="noopener noreferrer"&gt;&lt;code&gt;pgvector&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://medium.com/@wtaisen/hnsw-indexing-in-vector-databases-simple-explanation-and-code-3ef59d9c1920" rel="noopener noreferrer"&gt;HNSW indexes&lt;/a&gt;) to build RAG (Retrieval Augmented Generation) pipelines. The recent Databricks and Snowflake acquisitions show that engineers increasingly value an "all-in-one" platform rather than a standalone OLAP solution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are new to Postgres, I recommend &lt;a href="https://www.youtube.com/@hnasr" rel="noopener noreferrer"&gt;Hussein Nasser&lt;/a&gt;'s YouTube channel, which has a detailed video explaining the Postgres internals.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Q56kljmIN14"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision Points ⚖️
&lt;/h3&gt;

&lt;p&gt;This section raises a set of questions I would consider when evaluating a Postgres provider to reduce a lot of future pain points. They are not ordered by priority.&lt;/p&gt;

&lt;h4&gt;
  
  
  Query Patterns ❓
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;What is the expected ratio of reads and writes - is a single instance sufficient? &lt;em&gt;(Does the provider offer a range of instance sizes? Are there read replicas, connection poolers or sharding solutions?)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;What periods of inactivity am I expecting? &lt;em&gt;(Serverless is more economically-viable for spiky workloads. In practice, when baseline traffic varies by at least 50% from its peak.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;What levels of transaction isolation will I need? &lt;em&gt;(This rules out a few distributed offerings.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Does the provider have global endpoints / multi-region support? &lt;em&gt;(Local benchmarks are no good if your database is on the other side of the world).&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Compatibility 💘
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Do we need runtime compatibility, wire-protocol, or &lt;a href="https://www.yugabyte.com/postgresql/compare-postgresql-compatibility/" rel="noopener noreferrer"&gt;something in between&lt;/a&gt;? &lt;em&gt;(How many Postgres features will I have support for?).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;What service limitations exist? &lt;em&gt;(e.g., &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraPostgreSQLReleaseNotes/AuroraPostgreSQL.Extensions.html" rel="noopener noreferrer"&gt;Limits&lt;/a&gt; on database object counts, concurrent connections, foreign keys, transaction size or timings).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;What extensions are available? &lt;em&gt;(e.g., Cloud platforms often expose only a &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraPostgreSQLReleaseNotes/AuroraPostgreSQL.Extensions.html" rel="noopener noreferrer"&gt;subset&lt;/a&gt;).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Are system tables accessible, or can I easily monitor diagnostic information? &lt;em&gt;(e.g., &lt;code&gt;pg_stat-*&lt;/code&gt; tables).&lt;/em&gt; &lt;/li&gt;
&lt;li&gt;What integrations exist with existing services? &lt;em&gt;(e.g., Cloud providers offer archival or "zero-ETL" features).&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Control 💪
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Does a &lt;code&gt;superuser&lt;/code&gt; exist? &lt;em&gt;(Some platforms &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.html" rel="noopener noreferrer"&gt;do not provide&lt;/a&gt; access).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;How portable is my data? &lt;em&gt;(Can the underlying backups and data files be exported externally?).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;How much control do we need over major versions' adoption timelines? &lt;em&gt;(Vendors often enforce a &lt;a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/extended-support.html" rel="noopener noreferrer"&gt;support window&lt;/a&gt;).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;What methods are supported for upgrades? &lt;em&gt;(e.g., In-place, logical replication, zero-downtime. Providers like YugabyteDB claim to have zero-downtime upgrades. Others need &lt;a href="https://www.instantdb.com/essays/pg_upgrade" rel="noopener noreferrer"&gt;DIY solutions&lt;/a&gt;).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;What is the pricing model? &lt;em&gt;(Is billing broken down by storage, compute and I/O?).&lt;/em&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Security &amp;amp; Risk 🎲
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;How much trust do you place in the company in the long term? &lt;em&gt;(Cloud providers may sunset services but have better longevity. Newer providers may withdraw their free tier, or be acquired and change pricing).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;What &lt;a href="https://www.crunchydata.com/blog/data-encryption-in-postgres-a-guidebook" rel="noopener noreferrer"&gt;encryption&lt;/a&gt; and security models exist? &lt;em&gt;(Is my infrastructure pooled or isolated? Can I rotate encryption keys used?).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Is there support for multi-tenancy or Row-Level Security (RLS)? &lt;em&gt;(&lt;a href="https://www.thenile.dev/" rel="noopener noreferrer"&gt;Nile&lt;/a&gt; aims to address this for B2B SaaS firms).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;What SLAs exist and is downtime compensated? &lt;em&gt;(e.g., During scaling events / maintenance windows).&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Starting Point 📍
&lt;/h3&gt;

&lt;p&gt;Having raised the important questions, it is clear that there is a lot to think about upfront. In the absence of any other information (and to avoid being overwhelmed!), I would adopt these principles.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stay close to the roots 🪾&lt;/strong&gt;: Maintain runtime compatibility with standard PosrgreSQL for as long as possible. This makes local testing and feedback loops much quicker, and means faster adoption of new versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Benchmark 📐&lt;/strong&gt;: Measure against your own workloads - do not rely solely on online benchmarks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid sprawl 🐙&lt;/strong&gt;: Default to Postgres for new use cases before reaching for new platforms or databases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Know your options 🧠&lt;/strong&gt;: Periodically evaluate which migration paths exist, their upgrade timelines, tenancy models and trends.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once a solution is adopted at scale, migration to a new setup is always possible, but it takes significant effort (just ask &lt;a href="https://www.figma.com/blog/how-figmas-databases-team-lived-to-tell-the-scale/" rel="noopener noreferrer"&gt;Figma&lt;/a&gt;). There is no way to know the future, but with these principles in mind you will do better than most at adopting the right flavour of Postgres for your use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary 🧵
&lt;/h3&gt;

&lt;p&gt;In this post, we revisited the advice to &lt;em&gt;"just use Postgres"&lt;/em&gt; refreshed for 2025, in the broader context of acquisitions and recent product launches. The statement still holds true, but some nuance exists to answer the question of &lt;em&gt;"what next?"&lt;/em&gt;. These questions form a practical checklist for evaluating Postgres providers.&lt;/p&gt;

&lt;p&gt;It is an exciting time to be a developer working with Postgres, as the technology continues to evolve almost 40 years after its &lt;a href="https://www.postgresql.org/docs/7.0/intro60.htm" rel="noopener noreferrer"&gt;inception at Berkeley&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In future posts, I may dive into some of the &lt;a href="https://www.avestura.dev/blog/explaining-the-postgres-meme" rel="noopener noreferrer"&gt;idiosyncrasies&lt;/a&gt; of Postgres or dive into systems like AWS' DSQL.  There are some great creators in the space which make it far easier to keep up to date in the meantime. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🎧 &lt;a href="https://postgres.fm/" rel="noopener noreferrer"&gt;Postgres FM&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🎧 &lt;a href="https://www.scalingpostgres.com/" rel="noopener noreferrer"&gt;Scaling Postgres&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🎧 &lt;a href="https://talkingpostgres.com/" rel="noopener noreferrer"&gt;Talking Postgres&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Get in touch 📧
&lt;/h3&gt;

&lt;p&gt;As I say in my &lt;a href="https://chrisdavies.dev/about/" rel="noopener noreferrer"&gt;About&lt;/a&gt; page, I would love to hear from you. If you got to the end of this post and have anything to share, please get in touch on &lt;a href="https://www.linkedin.com/in/c-j-davies/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://x.com/c_davies21" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>database</category>
      <category>postgres</category>
      <category>architecture</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Call My Agent 🤖</title>
      <dc:creator>Chris Davies</dc:creator>
      <pubDate>Sat, 28 Jun 2025 09:03:20 +0000</pubDate>
      <link>https://dev.to/chris_davies/call-my-agent-4a67</link>
      <guid>https://dev.to/chris_davies/call-my-agent-4a67</guid>
      <description>&lt;p&gt;📝 &lt;strong&gt;This is cross-posted from my personal site - &lt;a href="https://chrisdavies.dev" rel="noopener noreferrer"&gt;https://chrisdavies.dev&lt;/a&gt; .&lt;/strong&gt;  &lt;/p&gt;




&lt;p&gt;This week, we take a brief step into the world of GenAI Agents, having read "&lt;em&gt;&lt;a href="https://mastra.ai/book" rel="noopener noreferrer"&gt;Principles of Building AI Agents&lt;/a&gt;&lt;/em&gt;" by &lt;a href="https://www.linkedin.com/in/sambhagwat/" rel="noopener noreferrer"&gt;Sam Bhagwat&lt;/a&gt;. 📚 &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The title is a terrible pun on a French &lt;a href="https://en.wikipedia.org/wiki/Call_My_Agent!" rel="noopener noreferrer"&gt;TV show&lt;/a&gt;...&lt;/em&gt; 📺&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Book 📖️
&lt;/h3&gt;

&lt;p&gt;I first learned about this book from a &lt;a href="https://x.com/GergelyOrosz/status/1926342778012786853" rel="noopener noreferrer"&gt;Tweet&lt;/a&gt; ("X"?) by &lt;a href="https://www.pragmaticengineer.com/" rel="noopener noreferrer"&gt;Gergely Orosz&lt;/a&gt;. Out of curiosity, I downloaded the e-book and Sam kindly reached out to send across a physical copy. The need for a second edition just months after the first highlights how fast this space is moving.&lt;/p&gt;

&lt;p&gt;It is a short read (2-3 hours) for technical readers that introduces &lt;em&gt;a lot&lt;/em&gt; of breadcrumbs for people to go away and read more on. This means that by the end, you are not an expert, but carry a lot of the terms you need to do research and have conversations about the topic. It uses a top-down approach, building on top of LLMs, not explaining their foundations.&lt;/p&gt;

&lt;p&gt;The book is available in &lt;a href="https://mastra.ai/book" rel="noopener noreferrer"&gt;virtual&lt;/a&gt; or &lt;a href="https://www.amazon.co.uk/Principles-Building-Agents-Sam-Bhagwat/dp/B0DYH5GHDD/ref=sr_1_1" rel="noopener noreferrer"&gt;physical&lt;/a&gt; form.&lt;/p&gt;

&lt;p&gt;I am &lt;em&gt;not&lt;/em&gt; being sponsored to write this - I just think it is great organic marketing and enjoy shorter reads. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://mastra.ai/" rel="noopener noreferrer"&gt;Mastra&lt;/a&gt; is a company building a framework for building AI Agents and orchestrating them with workflows. Sam is a co-founder of the &lt;a href="https://www.gatsbyjs.com/docs" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt; web framework, and is taking an interesting bet on the TypeScript community as the place to start, with some early adoption &lt;a href="https://www.stackone.com/blog/a-new-direction-for-tool-orchestration" rel="noopener noreferrer"&gt;examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the next few sections, I'll go over some terminology and takeaways.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy50dzwvigf9zuyq4wee9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy50dzwvigf9zuyq4wee9.jpg" alt="Principles of Building AI Agents"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;em&gt;With thanks to &lt;a href="https://www.linkedin.com/in/sambhagwat/" rel="noopener noreferrer"&gt;Sam Bhagwat&lt;/a&gt;&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  My Two Cents 🪙🪙
&lt;/h3&gt;

&lt;p&gt;Like most waves of change and hype in the tech industry, the GenAI topic (coined as "&lt;a href="https://www.nextbigfuture.com/2025/06/software-3-0-by-karpathy.html" rel="noopener noreferrer"&gt;Software 3.0&lt;/a&gt;") causes &lt;a href="https://www.elastic.co/blog/generative-ai-hype-myths-it-leaders" rel="noopener noreferrer"&gt;divisiveness&lt;/a&gt; and &lt;a href="https://www.mcsweeneys.net/articles/a-company-reminder-for-everyone-to-talk-nicely-about-the-giant-plagiarism-machine" rel="noopener noreferrer"&gt;scepticism&lt;/a&gt;. A lot of this reaction is justified.&lt;br&gt;
AI is not a new field - it has been around for decades. However, the release of ChatGPT in November 2022 created a fresh wave of noise around Generative AI.&lt;/p&gt;

&lt;p&gt;Not long ago, we had a hype cycle around Blockchain and Cryptocurrency, which did not cause the revolution it promised. Blockchain often felt like a solution in search of a problem. GenAI has a plethora of tangible &lt;a href="https://aws.amazon.com/ai/generative-ai/use-cases/" rel="noopener noreferrer"&gt;use cases&lt;/a&gt;, but needs to answer difficult questions in many areas before it can allay concerns. Namely surrounding the ethical, legal, socioeconomic, environmental and security impact of adopting this technology at scale. This is why the marketing can sometimes feel premature.&lt;/p&gt;

&lt;p&gt;We could place a lot of these hype cycles along different points of the &lt;a href="https://en.wikipedia.org/wiki/Gartner_hype_cycle" rel="noopener noreferrer"&gt;Gartner Hype Cycle&lt;/a&gt;. Blockchain and Cryptocurrency has found its niche after the hype has &lt;a href="https://www.cio.com/article/3838169/rip-finally-to-the-blockchain-hype.html" rel="noopener noreferrer"&gt;calmed down&lt;/a&gt;. GenAI could &lt;a href="https://techxplore.com/news/2025-06-ai-hype-blockchain-frenzy-dies.html" rel="noopener noreferrer"&gt;follow a similar path&lt;/a&gt; once the dust settles.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To avoid bloat, this post purely focuses on the &lt;em&gt;engineering discipline&lt;/em&gt; that the book introduces, rather than the impact of this technology or my personal use of it for &lt;a href="https://newsletter.pragmaticengineer.com/p/two-years-of-using-ai" rel="noopener noreferrer"&gt;productivity&lt;/a&gt;. I will save those for a future post.&lt;br&gt;
Criticism, if justified, needs to come from a place of education.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexzx1gvykcosar6rvg7x.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexzx1gvykcosar6rvg7x.jpg" alt="Gartner Hype Cycle"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;em&gt;Image credit: &lt;a href="https://www.gartner.com/en/insights/gartner-hype-cycle" rel="noopener noreferrer"&gt;Gartner&lt;/a&gt;&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Jargon Busting 🧑‍🏫
&lt;/h3&gt;

&lt;p&gt;The book introduces a lot of terminology. A true test of my understanding is whether I can now explain these concepts, which is what this section tries to achieve. Feel free to skip this section if you already have a good understanding.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Disclaimer ⚠️&lt;/strong&gt;: I did have some background knowledge before reading this book, but not much. Academic readers might cringe at my attempt.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Large Language Model. Probabilistic model trained on natural language which, when given a training corpus and input, will predict the most likely output using mathematical operations (e.g., matrix multiplication). These can be &lt;em&gt;hosted&lt;/em&gt; or &lt;em&gt;open source&lt;/em&gt;.&lt;/td&gt;
&lt;td&gt;GPT-o1, Gemini, Llama&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Input to an LLM (commonly textual). These can be &lt;em&gt;system&lt;/em&gt; or &lt;em&gt;user&lt;/em&gt; level, which alters the scope (e.g. chat-level or individual query respectively). Prompt engineering is the practice of tailoring prompts to improve outputs.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"ChatGPT is cool!"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Token&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single raw chunk of text, taken from a larger document or textual input.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;["Chat", "G", "PT", " is", " cool", "!"]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Embedding / Vector&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transformed &lt;code&gt;N&lt;/code&gt;-dimensional data structure representing the &lt;em&gt;semantic meaning&lt;/em&gt; of a token or document. &lt;code&gt;N&lt;/code&gt; is typically ~1536. This structure is used for &lt;em&gt;similarity searches&lt;/em&gt; in &lt;code&gt;N&lt;/code&gt;-dimensional space, which gives a numerical reading on how relevant the stored content is to the input prompt.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[0.12, ..., 0.95]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context Window&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The number of text chunks (tokens in the input and output) which the LLM can handle at any one time when processing a query.&lt;/td&gt;
&lt;td&gt;GPT-4 = ~128k tokens.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A long-lived process that uses LLMs to perform tasks. They are allocated memory, invoke tools, access resources, and maintain context over time.&lt;/td&gt;
&lt;td&gt;AI assistant that books meetings.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model Context Protocol. Defined by &lt;a href="https://www.anthropic.com/news/model-context-protocol" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt; which stipulates how an agent should retrieve information from a third-party source. An MCP Server (hosted locally or remotely) declares its &lt;em&gt;prompts&lt;/em&gt;, &lt;em&gt;tools&lt;/em&gt; and &lt;em&gt;resources&lt;/em&gt; for MCP clients to use.&lt;/td&gt;
&lt;td&gt;Agent that is given instructions for connecting to a database to find weather information.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tool (MCP)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Method for the agent to perform a task e.g. a web resource lookup, or calling another Agent.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fetch_weather(city)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resource (MCP)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data source used by the agent when performing tasks e.g. a database.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;city_db&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt (MCP)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Templated instructions for the agent to perform a task e.g. details needed to call a tool or read from a resource.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"Search {query} in {resource}"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A2A&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent To Agent Protocol. Defined by &lt;a href="https://developers.googleblog.com/en/a2a-a-new-era-of-agent-interoperability/" rel="noopener noreferrer"&gt;Google&lt;/a&gt; which stipulates how an Agent should communicate with another Agent, given that they are likely to have been built independently.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Agent1&lt;/code&gt; asking &lt;code&gt;Agent2&lt;/code&gt; to extract data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Retrieval Augmented Generation. A method for enhancing the information available to an LLM by performing on-demand &lt;em&gt;embedding&lt;/em&gt; and &lt;em&gt;similarity search&lt;/em&gt; with a vector database, to enrich a prompt with added stored context.&lt;/td&gt;
&lt;td&gt;Retrieval of FAQs before answering queries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vector Database&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A data store specialised in storage and retrieval of &lt;code&gt;N&lt;/code&gt;-dimensional vectors.&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/pgvector/pgvector/" rel="noopener noreferrer"&gt;pgvector&lt;/a&gt;, &lt;a href="https://www.pinecone.io/" rel="noopener noreferrer"&gt;Pinecone&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agentic Workflow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;An emerging practice which defines the tasks agents perform as steps in a deterministic orchestration workflow - commonly as a graph.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Fetch doc → summarise → email&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Guardrail&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Measures which inject additional context in the prompts given to LLMs to minimise the risk of them producing insecure or malicious output. More of a &lt;em&gt;guidance&lt;/em&gt; mechanism than a watertight security measure.&lt;/td&gt;
&lt;td&gt;Requesting that PII is not returned.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Eval&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Methods for evaluating the effectiveness and accuracy of an Agent.&lt;/td&gt;
&lt;td&gt;&lt;a href="https://cookbook.openai.com/examples/evaluation/getting_started_with_openai_evals" rel="noopener noreferrer"&gt;OpenAI Evals&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you are a visual learner, then I am a fan of the &lt;a href="https://www.youtube.com/@ByteByteGo" rel="noopener noreferrer"&gt;ByteByteGo&lt;/a&gt; YouTube channel, which has a short video explaining some of these concepts.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/eHEHE2fpnWQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Snippets ✂️
&lt;/h3&gt;

&lt;p&gt;Despite being a short read, the book is full of advice from practical experience. This section summarises the main lessons.&lt;/p&gt;

&lt;h4&gt;
  
  
  Non-Determinism 🎲
&lt;/h4&gt;

&lt;p&gt;Embrace not being sure of the answer. The book makes it very clear from the start that adopting this technology means being comfortable with non-determinism. A lot of Software Engineering is made easier by ensuring our programs exhibit repeatable, deterministic behaviour, so this introduces new challenges. In this field, the fundamental building blocks are &lt;em&gt;probabilistic&lt;/em&gt;. The same input may generate infinite variations of output. Going back to the "&lt;a href="https://www.nextbigfuture.com/2025/06/software-3-0-by-karpathy.html" rel="noopener noreferrer"&gt;Software 3.0&lt;/a&gt;" concept, even "2.0" (ML and Neural Nets) can be made deterministic (by fixing weights and parameters), so this marks a complete departure.&lt;/p&gt;

&lt;p&gt;The idea of creating &lt;a href="https://www.geeksforgeeks.org/artificial-intelligence/static-vs-dynamic-environment-in-ai/" rel="noopener noreferrer"&gt;&lt;em&gt;dynamic&lt;/em&gt; agents&lt;/a&gt; takes this one step further, as it might alter the agent environment such as the native spoken language and the LLM in use (based on business rules). This explodes the number of possible execution contexts. &lt;/p&gt;

&lt;p&gt;Testing looks very different in a non-deterministic world. Instead of pass/fail semantics, Evals provide an interval of confidence (&lt;code&gt;{0,1}&lt;/code&gt;) by which we can measure accuracy and reliability, and pick up on meaningful regressions or improvements. Because of this complexity, organizations often find it simpler to do A/B testing in production by measuring signals of user behaviour after deployment as a measure of effectiveness by proxy.   &lt;/p&gt;

&lt;p&gt;Because of all this non-determinism at the inner layer, imposing a graph-like structure on agentic workflows seems like a sensible method of constraining the probability space.&lt;/p&gt;

&lt;h4&gt;
  
  
  Size vs Cost vs Performance 💰
&lt;/h4&gt;

&lt;p&gt;Start simple, then optimise. A trade-off is required between cost, performance and accuracy of the underlying models. Cheaper models will produce fast, less accurate responses, which might be enough for some use cases. The book advises starting with a costlier model (for greater accuracy) whilst volumes are low, then optimising. One should start by exhausting the LLM's context window, before building agents, workflows or RAG pipelines.&lt;/p&gt;

&lt;p&gt;There are clever UX techniques one might employ to make it look like the agent is doing useful work before it outputs a final answer, showing its workings and reasoning. A few solutions mentioned in the book stream their responses to a client via web-sockets, making use of tools like &lt;a href="https://electric-sql.com/" rel="noopener noreferrer"&gt;ElectricSQL&lt;/a&gt; before a full answer is generated. &lt;/p&gt;

&lt;h4&gt;
  
  
  Hosted vs Open-Source 🥷
&lt;/h4&gt;

&lt;p&gt;As for the choice between &lt;em&gt;hosted&lt;/em&gt; versus &lt;em&gt;open source&lt;/em&gt; models (e.g., &lt;a href="https://www.anthropic.com/claude" rel="noopener noreferrer"&gt;Claude&lt;/a&gt; vs &lt;a href="https://deep-seek.chat/" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;), it is a natural reaction to want to see the inner workings of these models. I am reminded of the choice organizations need to make around adopting cloud computing. A &lt;em&gt;hosted&lt;/em&gt; model provides much less of the hassle in getting up and running with this technology, which doesn't differentiate a business from its competitors. On the surface there is not as much vendor lock-in (&lt;em&gt;yet&lt;/em&gt;) compared to public cloud, but that may change. Each model has their own strengths, meaning that once a production system is tuned, it is hard to justify the effort to migrate. Some systems adopt a hybrid of models and &lt;a href="https://arxiv.org/html/2502.00409v1" rel="noopener noreferrer"&gt;route prompts&lt;/a&gt; based on rules (which is the equivalent of going "multi-cloud" to reduce concentration risk in my analogy).  &lt;/p&gt;

&lt;h4&gt;
  
  
  Prompt Techniques 🔁
&lt;/h4&gt;

&lt;p&gt;Structure matters. In most models there is a meaningful difference in output between "&lt;em&gt;Zero-Shot&lt;/em&gt;" and "&lt;a href="https://www.promptingguide.ai/techniques/cot" rel="noopener noreferrer"&gt;&lt;em&gt;Chain Of Thought&lt;/em&gt;&lt;/a&gt;" prompting. Some models are specifically designed for &lt;a href="https://www.latent.space/p/o1-skill-issue" rel="noopener noreferrer"&gt;everything-upfront&lt;/a&gt; interactions, rather than a chat. Much like a human would work better solving a problem if it had some examples, hints, and a schema (e.g., XML, JSON) to work with, so do LLMs.   &lt;/p&gt;

&lt;p&gt;The book also recommends trying a "&lt;em&gt;seed crystal&lt;/em&gt;" approach, which involves asking an LLM how it should be prompted to do a task. Think of it like "meta-prompting". &lt;/p&gt;

&lt;h4&gt;
  
  
  Infrastructure 🛠️
&lt;/h4&gt;

&lt;p&gt;Agents are stateful and resource intensive. This constrains the infrastructure that companies can use to host them. Most serverless infrastructure will buckle under their resource usage, or time-out within 15 minutes. With the move to represent agents as workflows (e.g. &lt;a href="https://mastra.ai/" rel="noopener noreferrer"&gt;Mastra&lt;/a&gt;, &lt;a href="https://www.langchain.com/langgraph" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt;), I do wonder if there is space to move orchestration into the infrastructure, much like we have seen with solutions like AWS &lt;a href="https://aws.amazon.com/step-functions/" rel="noopener noreferrer"&gt;Step Functions&lt;/a&gt;, which replaces orchestration in code. If a breakthrough were to happen in this space, it would change the cost-profile of this technology, linking it more dynamically to usage. This is much more attractive for firms with limited budgets (e.g. start-ups).&lt;/p&gt;

&lt;p&gt;There are a multitude of ways to orchestrate and run agents, which makes it harder to move this concern to the infrastructure level.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft5v9cz5of74i8w6c9aza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft5v9cz5of74i8w6c9aza.png" alt="Types of Multi Agent architectures"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Example multi-agent architectures. Image credit: &lt;a href="https://mastra.ai/book" rel="noopener noreferrer"&gt;&lt;em&gt;Principles of Building AI Agents&lt;/em&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Takeaways ✍️
&lt;/h3&gt;

&lt;p&gt;Having read the book and surrounding articles, I have some principles which I would adopt if I was to build a production agentic system. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Measure 📐&lt;/strong&gt;: Ensure that the technology solves a real problem (e.g. reducing time or cognitive load in manual processes). Make observability a day 1 concern (e.g., tool usage, task completion times). Do not rely solely on Eval scores - use metrics that describe user behaviour or feature adoption rates. Use feature flags and circuit breakers to rollback if metrics suggest a negative impact. &lt;a href="https://incident.io/building-with-ai/controlling-costs" rel="noopener noreferrer"&gt;Understand costs&lt;/a&gt; before the system grows. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build Incrementally 🛠️&lt;/strong&gt;: Prioritise slower, more accurate models when volumes are low. Constrain possible outcomes by building workflows with very small scope tasks (e.g., each agent can access at most 1 tool).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-First 🧍‍♂️&lt;/strong&gt;:  Keep humans in the loop. Don't make Agents the only option (&lt;a href="https://techinformed.com/is-klarnas-scale-back-on-ai-a-turning-point-for-cx/" rel="noopener noreferrer"&gt;Klarna&lt;/a&gt; can attest here). Prioritise assistance over autonomy, allowing human overrides. Avoid introducing &lt;a href="https://www.complexcognition.co.uk/2021/06/ironies-of-automation.html" rel="noopener noreferrer"&gt;more risks than you started with&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security-First 🔐&lt;/strong&gt;: Don't rely on Guardrails as the only layer of defence. Do not &lt;a href="https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/" rel="noopener noreferrer"&gt;expose data sources containing private data&lt;/a&gt; (even if you think the LLM has been told not to access it). Adopt AuthN/AuthZ techniques at each layer that allows for it. Store and rotate API keys as secrets outside the agent's control. Physically isolate any data which does not serve a useful purpose for the task at hand. Monitor traffic (e.g. queries made to tools) and deny agents' tool access based on anomalous activity (e.g. request limits breached, or a spike in "permission denied" errors).
&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Consensus 🌍
&lt;/h4&gt;

&lt;p&gt;The current state of protocols in this space feels very much like an &lt;em&gt;xkcd&lt;/em&gt; comic. Large companies are commercially incentivised to be the first to claim a standard, which leaves them half-defined on initial publication, relying on &lt;a href="https://aaronparecki.com/2025/04/03/15/oauth-for-model-context-protocol" rel="noopener noreferrer"&gt;community contributions&lt;/a&gt; to improve.&lt;/p&gt;

&lt;p&gt;In more established domains, we have neutral organizations which provide trust. &lt;a href="https://www.iana.org/" rel="noopener noreferrer"&gt;IANA&lt;/a&gt; allocates IP addresses. The &lt;a href="https://www.cve.org/" rel="noopener noreferrer"&gt;CVE&lt;/a&gt; registry standardises tracking of security vulnerabilities. &lt;a href="https://soc2.co.uk/" rel="noopener noreferrer"&gt;SOC2&lt;/a&gt; offers a set of controls for how organizations handle security and data. We would benefit from the same kind of unbiased, centralised certification for of what makes an Agent approved for production use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsqk233odna8c755qk5c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsqk233odna8c755qk5c.png" alt="XKCD Comic on Competing Standards"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;em&gt;Image credit: &lt;a href="https://xkcd.com/927/" rel="noopener noreferrer"&gt;xkcd&lt;/a&gt;&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary 🧵
&lt;/h3&gt;

&lt;p&gt;In this post, we reviewed the terminology and topics discussed in "&lt;em&gt;&lt;a href="https://mastra.ai/book" rel="noopener noreferrer"&gt;Principles of Building AI Agents&lt;/a&gt;&lt;/em&gt;" by &lt;a href="https://www.linkedin.com/in/sambhagwat/" rel="noopener noreferrer"&gt;Sam Bhagwat&lt;/a&gt;. If this blog sounded interesting, I'd recommend downloading or buying the book directly, and following their work.&lt;/p&gt;

&lt;p&gt;It is my first meaningful deep-dive into the engineering practices behind this technology. This space is clearly changing rapidly. If we look back on this post in 1-2 years, I would be interested to see what changes. Perhaps the majority of future use cases &lt;a href="https://arxiv.org/pdf/2506.02153" rel="noopener noreferrer"&gt;won't even need an LLM&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As for next steps, there are some free courses on the topic by the likes of &lt;a href="https://mastra.ai/course" rel="noopener noreferrer"&gt;Mastra&lt;/a&gt; and &lt;a href="https://huggingface.co/learn/agents-course/unit0/introduction" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;, which I may dive into. I was thinking of writing a blog on how AI is changing my day-to-day role, but &lt;a href="https://annievella.com/posts/the-software-engineering-identity-crisis/" rel="noopener noreferrer"&gt;this article&lt;/a&gt; says it far better than I could. &lt;/p&gt;

&lt;p&gt;If you have got to the end of this post and have recommendations, differing views or stories from real systems, please do get in touch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get in touch 📧
&lt;/h3&gt;

&lt;p&gt;As I say in my &lt;a href="https://chrisdavies.dev/about/" rel="noopener noreferrer"&gt;About&lt;/a&gt; page, I would love to hear from you. If you got to the end of this post and have anything to share, please get in touch on &lt;a href="https://www.linkedin.com/in/c-j-davies/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://x.com/c_davies21" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>genai</category>
      <category>programming</category>
      <category>books</category>
    </item>
    <item>
      <title>Java at 30 🎂</title>
      <dc:creator>Chris Davies</dc:creator>
      <pubDate>Sat, 21 Jun 2025 10:51:58 +0000</pubDate>
      <link>https://dev.to/chris_davies/java-at-30-do7</link>
      <guid>https://dev.to/chris_davies/java-at-30-do7</guid>
      <description>&lt;p&gt;📝 &lt;strong&gt;This is my first blog post, cross-posted from my personal site - &lt;a href="https://chrisdavies.dev" rel="noopener noreferrer"&gt;https://chrisdavies.dev&lt;/a&gt; .&lt;/strong&gt;  &lt;/p&gt;




&lt;p&gt;In this post: the features I love most about Java in 2025, and the wishlist I’d submit for the next 30 years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Happy Birthday? 🎈
&lt;/h3&gt;

&lt;p&gt;I have written &lt;a href="https://dev.java/" rel="noopener noreferrer"&gt;Java&lt;/a&gt; code for most of my professional career. On 23rd May 2025, it celebrated its &lt;strong&gt;30th birthday&lt;/strong&gt;. The language is older than I am, and has undeniably stood the test of time. &lt;/p&gt;

&lt;p&gt;Yet, in most development circles it gets a rather negative reception. Despite infamously powering &lt;a href="https://skeptics.stackexchange.com/questions/9870/do-3-billion-devices-run-java" rel="noopener noreferrer"&gt;"over 3 billion devices"&lt;/a&gt; and providing the secret sauce for some of the &lt;a href="https://careerkarma.com/blog/who-uses-java/" rel="noopener noreferrer"&gt;world's biggest companies&lt;/a&gt;, it is still seen as verbose and complex.&lt;br&gt;
This isn't controversial - there are enhancement &lt;a href="https://openjdk.org/projects/amber/design-notes/on-ramp" rel="noopener noreferrer"&gt;proposals&lt;/a&gt; dedicated to making Java a nicer language to learn.&lt;/p&gt;

&lt;p&gt;As with everything in technology, you need to use the right tool for the job, and there are paradigms that even modern Java can't live up to. That said, its recent developments have taken a lot of great features from other languages and given it a fresh lease of life.&lt;/p&gt;

&lt;p&gt;As we celebrate 30 years of the language, this blog post will explore some features I particularly admire, as well as a "wishlist" of features I have come across in other languages that would prove a very neat JEP in a future release.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3urmbgta7xini095cxa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs3urmbgta7xini095cxa.png" alt="Java at 30"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;em&gt;Image credit: &lt;a href="https://www.jetbrains.com/lp/java-30/" rel="noopener noreferrer"&gt;JetBrains&lt;/a&gt;&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Java in a Nutshell 🌰
&lt;/h3&gt;

&lt;p&gt;Earlier I spoke about the "right tool for the job" mantra. Being a general-purpose, Object-Oriented Programming (OOP) language, there are many use cases that Java is great for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Web Applications (Microservices)&lt;/strong&gt;: Servers which can cope with many thousands of requests using normal multi-threading or reactive frameworks. Simple HTTP applications can be built without frameworks, but enterprise-scale systems may benefit from using &lt;a href="https://spring.io/" rel="noopener noreferrer"&gt;Spring&lt;/a&gt;, &lt;a href="https://quarkus.io/" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt; or &lt;a href="https://micronaut.io/" rel="noopener noreferrer"&gt;Micronaut&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enterprise&lt;/strong&gt;: Traditional industries like banking and finance rely on a rich ecosystem of support for distributed APIs, queues, cryptography and persistence (JDBC).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile Applications&lt;/strong&gt;: Benefitting from its cross-platform "write once, run anywhere" tagline, Android applications primarily run on the JVM, via Kotlin or Java.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gaming&lt;/strong&gt;: Applications like Minecraft and Runescape.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Big Data&lt;/strong&gt;: Streaming technologies like Apache Kafka, Hadoop and Flink are all Java. Column stores like Apache Cassandra are also Java based. Large datasets can be explored using Elasticsearch, which is also (you guessed it!) Java.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That said, it requires more ceremony than other languages, which makes it a less popular choice in the following domains:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Latency-sensitive applications&lt;/strong&gt;: If manual memory management is important for performance, C++ or Rust is a far superior choice. Despite recent innovations with the Java Garbage Collector (e.g. &lt;a href="https://www.baeldung.com/jvm-zgc-garbage-collector" rel="noopener noreferrer"&gt;zgc&lt;/a&gt;) and start-up times (e.g. &lt;a href="https://openjdk.org/projects/crac/" rel="noopener noreferrer"&gt;CRaC&lt;/a&gt; and &lt;a href="https://www.graalvm.org/" rel="noopener noreferrer"&gt;GraalVM&lt;/a&gt;), Java suffers more than most languages due to heavy-GC pauses or cold starts in serverless environments. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scripting&lt;/strong&gt;: For cron jobs or CI/CD integration, just use Python, or (even better) Bash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data manipulation&lt;/strong&gt;: It takes a lot of code or third party libraries to manipulate data in common formats (e.g. CSV, JSON) and apply transforms. Using &lt;code&gt;pandas&lt;/code&gt;, &lt;code&gt;numpy&lt;/code&gt; or &lt;code&gt;scipy&lt;/code&gt; is more intuitive. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI and ML&lt;/strong&gt;: Despite recent innovations (e.g. &lt;a href="https://docs.langchain4j.dev/" rel="noopener noreferrer"&gt;LangChain4j&lt;/a&gt;), the bulk of community contributions to these fields are in Python. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web UIs&lt;/strong&gt;: You will be hard-pressed to find anyone willing to pick a fight for Swing or JavaFX over React, Angular or Next.js. For desktop applications, there is a bit more of a debate, though it is largely used in the IDEs themselves.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Powerful features ⚡️
&lt;/h3&gt;

&lt;p&gt;It goes without saying the impact that Java 8 had in 2014 was huge. Many people still describe it as the most revolutionary release we have had in the last 15 years. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;default&lt;/code&gt; methods blurred the line between Interfaces and Abstract Classes, but have proven very useful in the production code I have shipped, such as when rolling out a new method on an existing API. &lt;br&gt;
Lambdas, Streams and Optionals pushed Java into the functional domain more aggressively than ever, where it has been solidifying its place since. &lt;/p&gt;

&lt;p&gt;In most production code, we don't want side effects when we manipulate objects, and we want to capture data in composable but deconstructable patterns. This philosophy has given us records, sealed classes and powerful pattern matching semantics. It is great that one can now fully exhaust all options in a switch statement without falling back to a &lt;code&gt;default&lt;/code&gt; case and blowing up.  &lt;/p&gt;

&lt;p&gt;Use of &lt;code&gt;var&lt;/code&gt; is controversial in some circles (we weren't always duplicating type definitions both sides of the assignment). That said, IDEs' type inferences have made the transition easier to manage.&lt;/p&gt;

&lt;p&gt;Virtual threads provide a nice solution to tackle I/O intensive applications. In today's distributed world, I/O makes up a significant portion of most blocking code. Calling an HTTP endpoint, making an SDK call, executing a database transaction. All of these situations benefit from swapping out executors for ones which spin up virtual threads rather than platform threads. Just make sure you learn from &lt;a href="https://www.infoq.com/news/2024/08/netflix-performance-case-study/" rel="noopener noreferrer"&gt;others' mistakes&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;In the near future, Stream Gatherers will provide to intermediate operations what Collectors did for terminal operations. Whilst &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;filter&lt;/code&gt; and &lt;code&gt;reduce&lt;/code&gt; have been core staples of the Streams API for years, there are other useful execution paths. Window functions or stateful scans (e.g. moving averages or deduplication) to name a few. &lt;/p&gt;

&lt;p&gt;I am yet to be fully convinced on unnamed variables (&lt;code&gt;_&lt;/code&gt;) as I worry they will impact readability, but I am sure time will win me over like it did with &lt;code&gt;var&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Java Version&lt;/th&gt;
&lt;th&gt;Features&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lambdas, Streams, Optionals, Functional Interfaces, &lt;code&gt;default&lt;/code&gt; methods&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;9&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Modules, JShell&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;var&lt;/code&gt; keyword&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;11&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;HttpClient&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;12–16&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;switch&lt;/code&gt; expressions, Text blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;17&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;record&lt;/code&gt;, &lt;code&gt;sealed&lt;/code&gt; classes, Pattern matching with &lt;code&gt;instanceof&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;21&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Virtual threads, Pattern matching with &lt;code&gt;switch&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;22+&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stream Gatherers, Unnamed variables (&lt;code&gt;_&lt;/code&gt;), Statements before &lt;code&gt;super&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2yfufkdmo47py3flxe7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2yfufkdmo47py3flxe7.png" alt="JEPs over time"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;em&gt;Image credit: &lt;a href="https://www.infoq.com/news/2025/03/java24-released/" rel="noopener noreferrer"&gt;InfoQ&lt;/a&gt;&lt;/em&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Feature wishlist 💡
&lt;/h3&gt;

&lt;p&gt;Despite boasting an impressive list of recent and planned JEPs, there is still room for the language to grow. Based on my experience with other languages, there are a number of features that require a lot of boilerplate to support in Java. Native support for any of these would be welcome additions. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Disclaimer ⚠️&lt;/strong&gt;: This is my opinion as a language user working in an ideal world. I realise that most of this would require large overhauls and break backwards compatibility.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Enhanced Data Classes
&lt;/h4&gt;

&lt;p&gt;Immutable data classes finally came to Java with the advent of records. With a record, we have getters, &lt;code&gt;hashCode()&lt;/code&gt;, &lt;code&gt;equals()&lt;/code&gt; and &lt;code&gt;toString()&lt;/code&gt; out of the box. &lt;br&gt;
This is enough to deconstruct objects into their properties during &lt;code&gt;switch&lt;/code&gt; statements, which is coming soon. A natural extension to this would be support for validation, default values and serialisation options.&lt;br&gt;
Projects like &lt;a href="https://projectlombok.org/" rel="noopener noreferrer"&gt;Lombok&lt;/a&gt; and &lt;a href="https://www.joda.org/joda-beans/" rel="noopener noreferrer"&gt;Joda-Beans&lt;/a&gt; have aimed to bring this richness to Java for years, with limited adoption.&lt;/p&gt;

&lt;p&gt;A sketch of what this could look like is below - a data class with built-in validation and explicit support for JSON serialisation. This feature would accelerate most standard API development without requiring &lt;code&gt;JsonObject&lt;/code&gt; or &lt;code&gt;Gson&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Person&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@NotBlank&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;firstName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@NotBlank&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;lastName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@NotNegative&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Serializable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Map Streams
&lt;/h4&gt;

&lt;p&gt;Since Java 8, we have been able to stream and process data collections. This is readable and very effective for simple collections like &lt;code&gt;List&lt;/code&gt; or &lt;code&gt;Set&lt;/code&gt;. However, &lt;code&gt;Map&lt;/code&gt;s are not as easy to visualise in a Stream - we must use their &lt;code&gt;entrySet()&lt;/code&gt;, which means reasoning about a &lt;code&gt;Map.Entry&lt;/code&gt; at every step of the stream. &lt;br&gt;
OpenGamma's Strata project provides a &lt;a href="https://strata.opengamma.io/apidocs/com/opengamma/strata/collect/MapStream.html" rel="noopener noreferrer"&gt;full implementation&lt;/a&gt; (sketched below) of what first-class Map support in Streams could look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MapStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;underlying&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;MapStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MapStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;entrySet&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;MapStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;mapValues&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mapperFn&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* Omitted */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;MapStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;mapKeys&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mapperFn&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* Omitted */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;MapStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;filterKeys&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Predicate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;keyPredicate&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* Omitted */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;MapStream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;filterValues&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Predicate&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;valuePredicate&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* Omitted */&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;K&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;V&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;toMap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Explicit Mutable Collections
&lt;/h4&gt;

&lt;p&gt;Kotlin does a very good job at providing immutability by default. Users are encouraged to use &lt;code&gt;val&lt;/code&gt; by default, before falling back to &lt;code&gt;var&lt;/code&gt;.&lt;br&gt;
By extension, users have to opt in to mutable collections with the below factory methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;mutableListOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"3"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Java collections are mutable by default. &lt;code&gt;List.of(...)&lt;/code&gt; and &lt;code&gt;Set.of(...)&lt;/code&gt; use unmodifiable collections but do not make this obvious enough.&lt;/p&gt;

&lt;h4&gt;
  
  
  Delegate Methods
&lt;/h4&gt;

&lt;p&gt;It is a common pattern that we want to wrap an implementation of an interface in another. For example, a caching or rate-limiting implementation which wraps an underlying service.&lt;br&gt;
This separation of concerns is extremely powerful. To follow this pattern in Java, we must define a full implementation even if the behaviour we are overriding &lt;strong&gt;only applies to a subset of the methods&lt;/strong&gt;. In Kotlin we can avoid the boilerplate, by using delegates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;farewell&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleGreeter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;farewell&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Goodbye!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Partially override: only 'greet' is customized&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomGreeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Greeter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Greeter&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;greeter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"👋 Custom Hello!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// overrides only this&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;greeter&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CustomGreeter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SimpleGreeter&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;     &lt;span class="c1"&gt;// prints: 👋 Custom Hello!&lt;/span&gt;
    &lt;span class="n"&gt;greeter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;farewell&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// prints: Goodbye!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  String Templates
&lt;/h4&gt;

&lt;p&gt;When constructing large bodies of text, Java's multi-line text blocks have been a great addition. &lt;br&gt;
However, they are only readable if the full body of the text is static. Otherwise, we are forced to use &lt;code&gt;String.format(...)&lt;/code&gt; to interpolate values, which is hard to read when using many injected values. &lt;/p&gt;

&lt;p&gt;String Templates were briefly previewed in the JVM, but &lt;a href="https://nipafx.dev/inside-java-newscast-71/" rel="noopener noreferrer"&gt;later removed&lt;/a&gt;. I do hope that something like this comes to the language in a future release. In Kotlin (amongst other languages), we can place values directly where they are used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;payload&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
{
    "firstName": "$firstName",
    "lastName": "$lastName",
    "age": "$age"
}
"""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trimIndent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  List Comprehension
&lt;/h4&gt;

&lt;p&gt;Despite being harder to debug in long chains of calls, declarative Streams are more often than not easier to read than the imperative equivalent.&lt;br&gt;
However, a lot of Streams are a variant of &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;filter&lt;/code&gt; (or vice versa). I do wonder if we could borrow comprehensions from Python to make this a readable single line operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;123&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;class_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_get_class_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;class_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;mature_students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;class_info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;members&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;age&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Tuples
&lt;/h4&gt;

&lt;p&gt;There are lots of occasions where we might wish to work with a Pair or Tuple of values for data manipulation and extraction, without declaring an entire class for them. OpenGamma's Strata has &lt;a href="https://strata.opengamma.io/apidocs/com/opengamma/strata/collect/tuple/Pair.html" rel="noopener noreferrer"&gt;Pair&lt;/a&gt; and &lt;a href="https://strata.opengamma.io/apidocs/com/opengamma/strata/collect/tuple/Tuple.html" rel="noopener noreferrer"&gt;Tuple&lt;/a&gt; for this purpose, to make up for lack of support in the language. &lt;/p&gt;

&lt;p&gt;In Python, one can declare a &lt;code&gt;namedtuple&lt;/code&gt; with minimal ceremony and use them within the desired scope of the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;Account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;namedtuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Account&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;account_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;account_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_eligible_accounts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;all_accounts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_fetch_accounts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_accounts&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Primary&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Named Parameters
&lt;/h4&gt;

&lt;p&gt;When calling methods accepting the same type in multiple places in its signature, I believe named parameters would make life much easier and avoid ambiguity. Especially in environments where type inference is not available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;full_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_get_full_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Chris&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Davies&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_full_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Nullable Types
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;null&lt;/code&gt; references were once described as the &lt;a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/" rel="noopener noreferrer"&gt;billion-dollar mistake&lt;/a&gt;. Indeed, Java makes it very easy to obscure when values could be null. &lt;br&gt;
Despite the best efforts of the &lt;code&gt;Optional&lt;/code&gt; type, it just isn't suitable for use everywhere. It is a great solution when returning values, but not as we pass them around the application.   &lt;/p&gt;

&lt;p&gt;By explicitly marking an object as a nullable type variant, we can enforce checks in the IDE which tell a programmer when a null reference has not been accounted for. Various &lt;a href="https://github.com/google/guice/wiki/UseNullable" rel="noopener noreferrer"&gt;annotations&lt;/a&gt; exist to hint that types could be null or not null, but they are not doing static code analysis on the same level as other languages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;           &lt;span class="c1"&gt;// Optional — user may not provide one&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;dateOfBirth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;  &lt;span class="c1"&gt;// Optional — user may skip it&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;       &lt;span class="c1"&gt;// Optional with default value&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;printProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User ID: ${profile.id}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name: ${profile.name}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bio: ${profile.bio ?: "&lt;/span&gt;&lt;span class="nc"&gt;No&lt;/span&gt; &lt;span class="n"&gt;bio&lt;/span&gt; &lt;span class="n"&gt;yet&lt;/span&gt;&lt;span class="s"&gt;"}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email: ${profile.email ?: "&lt;/span&gt;&lt;span class="nc"&gt;Not&lt;/span&gt; &lt;span class="n"&gt;provided&lt;/span&gt;&lt;span class="s"&gt;"}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dateOfBirth&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Age: ${Period.between(it, LocalDate.now()).years}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DOB: Not provided"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Typed Strings
&lt;/h4&gt;

&lt;p&gt;In many APIs we want to enforce a domain over our identifier types, instead of letting them be free-form &lt;code&gt;String&lt;/code&gt;s. &lt;br&gt;
TypeScript brings type and set theory to the forefront of the language's design, which means it can do neat things like the below. &lt;code&gt;AccountId&lt;/code&gt; and &lt;code&gt;TransactionId&lt;/code&gt; are still treated as &lt;code&gt;string&lt;/code&gt;, but require very minimal declaration to bring safety to our APIs. No more passing in the wrong string values everywhere.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;AccountId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AccountId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TransactionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="na"&gt;brand&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TransactionId&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nf"&gt;deposit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transactionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TransactionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AccountId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Transaction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Result Types
&lt;/h4&gt;

&lt;p&gt;A large part of the negative perception Java gets is due to &lt;a href="https://www.baeldung.com/java-checked-unchecked-exceptions" rel="noopener noreferrer"&gt;Checked Exceptions&lt;/a&gt;. It is indeed clunky that a method might bubble up an &lt;code&gt;Exception&lt;/code&gt; through many calls, forcing application programmers to handle it. &lt;/p&gt;

&lt;p&gt;In Go, methods may return multiple objects, which allows us to check for an &lt;code&gt;Error&lt;/code&gt;. The code block &lt;code&gt;if err != nil&lt;/code&gt; is commonly seen as a result. Users are explicitly forced to consider that a return type could be an error at the call site, without bubbling it up further. &lt;br&gt;
TypeScript has &lt;a href="https://github.com/true-myth/true-myth" rel="noopener noreferrer"&gt;&lt;code&gt;true-myth&lt;/code&gt;&lt;/a&gt; for a similar purpose. OpenGamma's Strata has &lt;a href="https://strata.opengamma.io/apidocs/com/opengamma/strata/collect/result/Result.html" rel="noopener noreferrer"&gt;Result&lt;/a&gt;, which looks like the below.&lt;/p&gt;

&lt;p&gt;A native addition to the Java language would be welcome here. In most cases we want to face errors head-on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="no"&gt;R&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt; &lt;span class="n"&gt;failure&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Supplier&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;R&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;callFn&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="no"&gt;R&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;callFn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FailureType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ERROR&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error executing function"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isSuccess&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Decorators
&lt;/h4&gt;

&lt;p&gt;This section is largely about "syntactic-sugar". With &lt;code&gt;Function&lt;/code&gt;s, annotations and Aspect-Oriented Programming through frameworks like Spring, Java &lt;em&gt;could&lt;/em&gt; support this kind of behaviour. However, the ability for everyday programmers to make use of those tools is limited.&lt;br&gt;
Alternatively, Python supports a neat concept which allows users to wrap the execution of a method in custom logic, which can be done for abstracting performance logging/metrics, or other kinds of validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Print the runtime of the decorated function&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="nd"&gt;@functools.wraps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;perf_counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;run_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;end_time&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start_time&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Finished &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;() in &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run_time&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; secs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper_timer&lt;/span&gt;

&lt;span class="nd"&gt;@timer&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;waste_some_time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_times&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num_times&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Aside: Why does Java still beat other JVM languages' popularity? 🤔
&lt;/h3&gt;

&lt;p&gt;A natural question might be: &lt;em&gt;"If Java has largely copied its recent good features from other JVM languages, why did it not get replaced by them?"&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Indeed, &lt;a href="https://www.index.dev/blog/most-popular-programming-languages-" rel="noopener noreferrer"&gt;recent polling&lt;/a&gt; shows that it is standing strong compared to Kotlin, Scala, Groovy etc.&lt;br&gt;
An esteemed colleague of mine -- &lt;a href="https://github.com/jodastephen" rel="noopener noreferrer"&gt;Stephen Colebourne&lt;/a&gt; -- summarizes this far better than I ever could wish to in his 2024 &lt;a href="https://www.devoxx.co.uk/" rel="noopener noreferrer"&gt;Devoxx&lt;/a&gt; talk.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;TL;DW (Too Long; Didn't Watch)&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Had Kotlin been quicker to adopt a v1.0, we might all be writing Kotlin code today instead of Java.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/gFiZZw5BqOs"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Summary 🧵
&lt;/h3&gt;

&lt;p&gt;This post explored the state of Java at 30. From the powerful features I admire to a "wishlist" of future JEPs. With the success of the &lt;a href="https://www.baeldung.com/java-time-based-releases" rel="noopener noreferrer"&gt;6-month release cadence&lt;/a&gt;, the language keeps evolving while maintaining strong backward compatibility.  &lt;/p&gt;

&lt;p&gt;Regardless of whether you think we will be discussing its 50th or 60th birthday in years to come (&lt;em&gt;Spoiler: we definitely will&lt;/em&gt;), it is a very impressive piece of technology. I look forward to seeing how the language grows in the next 30 years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get in touch 📧
&lt;/h3&gt;

&lt;p&gt;As I say in my &lt;a href="https://chrisdavies.dev/about/" rel="noopener noreferrer"&gt;About&lt;/a&gt; page, I would love to hear from you. If you got to the end of this post and have anything to share, please get in touch on &lt;a href="https://www.linkedin.com/in/c-j-davies/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://x.com/c_davies21" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>programming</category>
      <category>firstpost</category>
      <category>coding</category>
    </item>
  </channel>
</rss>
