Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
157453 stories
·
33 followers

Apple says former employee exploited ‘rare’ bug to download confidential files after leaving for OpenAI

1 Share
Apple would not comment on the "security breach," which allegedly allowed a former employee to download sensitive files from Apple's network long after he departed the company for OpenAI.
Read the whole story
alvinashcraft
34 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Apple Reportedly Agreed to Intel Chips To Avoid White House Tariffs

1 Share
According to the Wall Street Journal (paywalled), Apple agreed to use Intel's U.S. chipmaking plants after White House officials pressured Tim Cook during tariff-relief talks last summer. MacRumors reports: In August 2025, Apple CEO Tim Cook was in Washington to lobby the Trump administration to drop its proposed 100 percent tariff on semiconductor imports -- a levy that would have raised costs across Apple's product line. Apple reportedly secured an exemption after pledging to invest hundreds of billions of dollars in the U.S., although many of those investments were already planned. During the meetings, president Trump and commerce secretary Howard Lutnick are said to have urged Cook to use Intel's fabrication plants to make some of Apple's chips. The link between the tariff talks and the Apple-Intel deal had not been previously reported. Almost a year later, Trump announced via his Truth Social platform that Apple would begin using Intel-made chips in some products. "We need to design and build our Chips right here in America," the president posted. The news sent Intel shares to record highs. According to a person familiar with the negotiations cited by the WSJ, Apple plans to have Intel make chips for both Mac laptops and iPhones. The report doesn't say which chips or in what volume, and Apple is expected to remain reliant on Taiwan Semiconductor Manufacturing Company, or TSMC, for the majority of its custom silicon.

Read more of this story at Slashdot.

Read the whole story
alvinashcraft
34 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Microsoft Entra ID security updates: Passkeys are the default authentication method in Entra ID

1 Share

As identity attacks grow more sophisticated in the AI era, organizations need stronger authentication methods that protect users from phishing, credential theft, and social engineering. To address these evolving threats, Microsoft Entra ID is updating its authentication experience by making passkeys the default phishing-resistant authentication method, helping customers reduce reliance on phishable methods such as SMS and voice.

Beginning September 1, 2026, Microsoft will begin rolling out passkeys as the default authentication experience in Microsoft Entra ID. As the rollout reaches each organization, users enabled for SMS or voice authentication will automatically be enabled for passkeys, and the next time they perform multifactor authentication, they’ll be prompted to register a passkey.

Following this transition, on February 1, 2027, Microsoft will retire Microsoft-provided telecom delivery for SMS and voice authentication and will no longer offer SMS and voice as a native Microsoft Entra capability. Organizations that still require SMS or voice authentication methods will have the option to choose one of our telecom partners through the Microsoft Security Store. Customers will be responsible for any associated telecom-related costs charged by the telecom partners.

We strongly recommend moving users to passkeys or another phishing-resistant authentication method as soon as possible.

Why stronger authentication matters in the AI era

Authentication methods that use SMS or voice rely on shared secrets or channels that attackers increasingly intercept, phish, or manipulate. Passkeys use public-key cryptography rather than shared secrets, making them phishing-resistant by design. They also provide a faster, simpler sign-in experience for users.

The case for moving beyond SMS and voice is no longer just that attackers intercept or socially engineer these methods. The threat environment has changed in speed, scale, and sophistication. Microsoft Threat Intelligence has observed AI-enabled phishing campaigns reaching click-through rates as high as 54%, compared with roughly 12% for more traditional campaigns, making stolen passwords and phishable second factors an urgent risk.1 At the same time, tactics such as SIM swapping and multifactor authentication bypass have become more accessible and repeatable.

An AI-powered cyberattack can use a compromised identity to automate discovery, privilege escalation, and lateral movement much faster than a human attacker working manually. This is why phishing-resistant authentication methods are so important.

By making passkeys the default authentication experience, organizations reduce reliance on phishable authentication methods and strengthen protection against credential theft and phishing.

Still need SMS or voice? Select a telecom provider in Microsoft Security Store 

Today, Microsoft provides the telecom delivery behind SMS and voice authentication natively within Entra ID. As part of this transition, we’ll step back from providing that native telecom delivery to encourage phishing-resistant methods as the standard for everyone.

For most organizations, the recommended path is simple: move users to passkeys at no additional cost.  

If you have a regulatory, technical, or business requirement to keep SMS or voice, you’ll be able to select, configure, and manage a third-party telecom provider through the Microsoft Security Store—a partner marketplace where you can contract directly with supported carriers. 

On September 18, 2026, we’ll share information on supported providers, deployment guidance, and technical documentation with pricing and commercial terms available through the Microsoft Security Store.

How to prepare

Start planning your transition now so you can select the deployment approach that best fits your organization and ensure your users are prepared for upcoming changes to their sign-in experience.

  1. Identify users who still use SMS or voice. Review your authentication method policy and identify which users or groups are enabled for SMS or voice authentication.
  2. Plan your passkey rollout. Enable passkeys and select the types that best fit your users’ devices and workflows. Microsoft Entra ID supports:
    • Synced passkeys, such as passkeys stored in platform credential managers like iCloud Keychain and Google Password Manager.
    • Device-bound passkeys, such as Microsoft Authenticator passkeys, Entra passkey on Windows, and FIDO2 security keys.
  3. Use registration campaign to drive adoption. Microsoft Entra ID can help organizations move users at scale by prompting them to register a passkey during multifactor authentication sign-in.
  4. Prepare user communications. Tell affected users what’s changing, when they’ll see a passkey registration prompt, and how to complete registration on their device.

For step-by-step guidance on planning, deploying, and managing passkeys, see our Microsoft Learn documentation and passkey deployment guide. 

If regulated, technical, or operational scenarios still require SMS or voice:

  1. Identify and document affected user segments.
  2. Starting October 30, 2026, select and configure a supported telecom provider through the Microsoft Security Store.
  3. Test your configuration with a pilot group before any broad rollout.

Timeline

DateMilestone
September 1, 2026 All users enabled for SMS or voice are auto-enabled and nudged for passkey registration upon multifactor authentication sign-in.

Use the passkey deployment guide to prepare your environment for passkey use. Notify affected users about the upcoming change. Ensure every user has a phishing-resistant authentication method, such as a passkey, Entra passkeys on Windows, or a FIDO2 security key.
September 18, 2026 Pricing, commercial terms, and a list of supported telecom providers will be shared.

If you plan to continue using SMS or voice authentication, review the available provider options and identify affected users. 
October 30, 2026 Admins may select and configure a supported telecom provider through the Microsoft Security Store. 
February 1, 2027 Microsoft-provided SMS and voice authentication ends.  

If SMS or voice remains necessary for specific users, configure a supported telecom provider before this date. 
After February 1, 2027 Users who use SMS or voice for multifactor authentication will be required to register a passkey before they can sign in. Automatic prompts to register a passkey will be enforced for all users in all tenants. There will be no opt-out option.

Note: The dates outlined in this post apply to Microsoft Entra ID in the public cloud only. Support for other cloud environments will follow on a separate timeline, with additional guidance and dates to be announced in advance.

SMS and voice have served their purpose well, bringing multifactor authentication to billions of users who otherwise would have had none. But the threat environment has evolved beyond their capabilities, and we need to evolve with it.

We’re making passkeys the default in Entra ID because they work better for users and worse for cyberattackers. We’re trying to make this transition as predictable as possible with clear dates, fallback options during migration, and recovery that doesn’t depend on phishable credentials anymore.

Learn more at aka.ms/passkeybydefault 

To learn more about Microsoft Security solutions, visit our website. Bookmark the Security blog to keep up with our expert coverage on security matters. Also, follow us on LinkedIn (Microsoft Security) and X (@MSFTSecurity) for the latest news and updates on cybersecurity.


1Microsoft Digital Defense Report 2025.

The post Microsoft Entra ID security updates: Passkeys are the default authentication method in Entra ID appeared first on Microsoft Security Blog.

Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Verifying Rust cryptography in SymCrypt, from standards to code

1 Share

How Rust, Lean, Aeneas, and AI agents are helping scale formal verification for production cryptographic algorithms

Diagram showing the process of verifying cryptographic code. An algorithm from a standard is converted into a formal specification, while Rust code is converted into a code model. The specification and code model are then compared through proof and verification steps.

At a glance

  • SymCrypt develops new verified cryptography using Rust, Aeneas, and Lean to provide higher security assurance.
  • We prove that their code safely and correctly implements standard algorithms, notably for post-quantum cryptography.
  • We are releasing verified code, specs, properties, and proofs initially for SHA-3 and ML-KEM. 
  • Aeneas allows verifying a large subset of Rust code and provides efficient automation in Lean to support the proof effort.
  • Agents allow scaling automation by writing proofs that are independently-verifiable.

Introduction and motivation for formal verification

Cryptographic code sits at the foundation of modern computing. It protects operating systems, cloud services, firmware, messaging systems, and the protocols that connect them. Small mistakes can have outsized consequences: a single arithmetic slip, missing bounds check, or incorrect state transition can undermine the security of an otherwise sound design.

Testing and auditing remain essential, but they are not enough on their own. Cryptographic implementations are often optimized, constant-time, architecture-specific, and deliberately low level. The code that ships rarely looks like the clean algorithm in a standard: it contains reductions, bit manipulations, SIMD intrinsics, carefully shaped loops, and portability layers for many environments.

Formal verification addresses this gap by deploying machine-checked proofs instead of relying on testing alone. Rather than merely checking that the code usually behaves correctly, verification implements a precise mathematical specification for all inputs that satisfy the stated preconditions.

In June last year, Microsoft announced we would formally verify new algorithms written in Rust in SymCrypt, the cryptographic provider used across products and services including Windows and Azure. New cryptographic implementations are being written in safe Rust, then verified in the Lean (opens in new tab) formal proof framework using the Aeneas (opens in new tab) toolchain. This applies in particular to post-quantum cryptography, which require fast secure implementations of complex algorithms. This combination gives us two layers of assurance: Rust rules out broad classes of memory-safety bugs, while Lean proofs establish functional correctness against formal specifications derived from standards.

The result is a new verification methodology for production cryptography: verify code as developers write it, preserve performance-oriented implementation choices, and make the proof process scalable enough to keep up with an evolving codebase.

Agents (stochastic, in blue) and tools (algorithmic, in green) for software verification. Human effort focuses on reviewing formalization of standards and main properties. Agents write proofs and intermediate properties. Compilation, code extraction, and proof verification are deterministic, not agentic.
Figure 1. Agents (stochastic, in blue) and tools (algorithmic, in green) for software verification. Human effort focuses on reviewing formalization of standards and main properties. Agents write proofs and intermediate properties. Compilation, code extraction, and proof verification are deterministic, not agentic.

Status of verification in SymCrypt

We have open sourced a SymCrypt branch (opens in new tab) that includes formal specifications and proofs. This public branch makes the proof artifacts available alongside the Rust algorithm implementations they validate, showing how the methodology applies to production cryptographic code. SymCrypt is not a standalone research prototype; it is Microsoft’s open-source cryptographic library used across products and services including Windows and Azure Linux.

This first release includes complete proofs for the Rust ML-KEM and SHA3 code that is being used in insiders builds of Windows today. SymCrypt is extending the same Rust, Lean, and Aeneas-based workflow to more Rust-native algorithms and integrating them into production versions for Windows and Linux, including for instance verified Rust code for, e.g., AES-GCM, FrodoKEM, and ML-DSA. The rest of this post uses this SymCrypt work as a concrete example, starting with how public standards become executable Lean specifications.

Turning standards into formal Lean specifications

The first step is to formalize what the algorithm is supposed to do. For cryptographic primitives, the source of truth is usually a public standard: a NIST specification, an IETF RFC, or another carefully reviewed algorithm description.

In our approach, the Lean specification is designed to stay close to the standard. When the standard describes a loop, an array update, or a mathematical operation, the Lean model follows the same structure wherever possible. This syntactic proximity matters: it makes the formal specification easier to audit because reviewers can compare the standard and the Lean side by side.

Lean also lets us write executable specifications. That means we can run the formal model against official test vectors to catch transcription errors, off-by-one mistakes, or misunderstandings of the standard. For algorithms such as ML-KEM, we can go further and prove high-level mathematical properties, such as showing that the formal model of the number-theoretic transform corresponds to the intended operation over the relevant polynomial ring.

A representative example is the number-theoretic transform (NTT) from ML-KEM. The standard describes the algorithm as an in-place transformation over 256 coefficients modulo q, with three nested loops that update pairs of coefficients using successive powers of the constant ζ (= 17).

Here is a direct translation of the NIST standard in Lean, trying to stick as close as possible to the original syntax:

The Lean version deliberately mirrors the structure of the standard: the same loop nest, the same zeta selection, and the same coefficient updates, allowing easy line-by-line human review. At the same time, it is executable and uses mathematical types, so it can be tested against known vectors and connected to higher-level theorems about the NTT’s algebraic meaning. In summary, the Lean specification is a concise, executable, mathematically meaningful model that tracks the standard closely enough to be reviewed by cryptographers and proof engineers alike.

Connecting the formal specification to the code

Once the specification is formalized, the next challenge is to connect it to the implementation. We do not ask developers to rewrite production cryptographic code in a verification-oriented language, nor do we generate code that product teams must then own. Instead, we verify the Rust code that engineers write, exactly as they write it.

Aeneas makes this possible by translating Rust’s mid-level representation into a pure Lean model. Rust’s ownership and borrowing discipline are crucial here. They let Aeneas safely eliminate much of the reasoning about pointer aliasing, liveness, and mutation that makes verification of C-style code so expensive.

For example, a Rust function that updates an array in place becomes, in Lean, a function that explicitly takes and returns a functional array. Mutable borrows are translated into value transformations. This preserves the behaviour that matters while presenting proof engineers with a functional model that is far easier to reason about.

Once in Lean, the function can be equipped with a theorem that states that it refines a formal specification. In other words, for every input satisfying the required bounds and well-formedness conditions, the implementation function returns the same mathematical result as the standard-derived Lean specification.

This style keeps responsibilities cleanly separated. Software engineers continue to write idiomatic, performant Rust. Verification engineers work against generated Lean models and prove theorems about them. The Rust code and the proofs live side by side, but the proof burden does not shape the code into something unnatural.

Going back to the NTT example, its Rust implementation is a function fn ntt(&mut [u16; 256]) that uses a mutable borrow to update an array in-place. The Lean translation purifies it into a function ntt : Array U16 256#usize → Result (Array U16 256#usize) that directly outputs the updated array, while wrapping it into a Result type to explicitly capture the fact that Rust functions may panic.

In this case, the theorem states that, if the array satisfies a well-formedness invariant (ensuring it represents a valid polynomial), then running the Rust model ntt returns the well-formed representation of the result of the mathematical specification Spec.ntt, modulo conversion from low-level arrays to high-level polynomials.

Scaling this to every function in real cryptographic code required substantial automation. Lean’s extensibility lets us build a gradient of automation with tactics for symbolic execution, arithmetic, arrays, and bit-vector reasoning. The experience becomes closer to debugging: automation handles the routine proof obligations, while engineers can inspect and refine the proof when a goal does not close automatically.

Supporting intrinsics and multiple architectures

Production cryptography cannot ignore hardware. SymCrypt must run across environments ranging from embedded and kernel contexts to cloud services. It also needs to take advantage of platform-specific instructions when they are available, including SIMD intrinsics and architecture-specific optimized paths.

A verification story that only works for a portable reference implementation is therefore incomplete. We need to verify the code that actually ships: dispatch logic, optimized routines, and target-specific variants included.

The code below is adapted from the ntt_layer  function that is internally used by the NTT. This function is compiled differently for x86-64 and aarch64, allowing dynamic dispatch to target-specific or portable implementations. On x86-64, it checks the availability of SSE2 instructions, while on aarch64 it checks for Neon.

As rustc’s output is inherently target specific, our toolchain compiles the code several times, one per compilation target for which verification is required, before merging the corresponding models. In effect, this merge operation turns the static dispatch permitted by the cfg attributes in the Rust code into a first layer of dynamic dispatch between x86-64 and aarch64 in the Lean model. Following what the Rust code does, these target specific models then themselves dynamically dispatch to the models of the XMM, Neon, and generic implementations.

Intrinsics require a slightly different treatment. Some low-level wrappers, especially those that manipulate raw pointers or expose platform instructions, are modelled by small, carefully reviewed Lean specifications. Others can be modelled using Rust code, which can be tested against hardware reference documentation, then translated and verified. The surrounding safe Rust code is then verified against those models. This keeps the trusted surface narrow while preserving the performance benefits of hardware acceleration.

The important point is that verification does not require giving up optimization. The methodology is designed to preserve the complexities of production code – including intrinsics, dispatch, and platform-specific implementations – while still proving a single, auditable correctness statement.

Reflecting formal guarantees to the code developer

Formal verification only scales in an engineering organization if developers can understand what has been proved. It is not enough for a proof to exist in a repository; the guarantee must be visible, reviewable, and synchronized to the code that engineers maintain.

To support this, we expose verification results through automatically generated dashboards. These dashboards summarize theorems in developer-facing terms: preconditions, postconditions, covered functions, trusted models, and remaining assumptions. Engineers do not need to open Lean to see what has been verified. For instance, below is the page displayed by the dashboard for our ntt function.

Screenshot of a verified formal specification for the symcrust::mlkem::ntt function. The page shows a green “Verified” badge, links to the Lean model and source code, and a specification stating the mathematical conditions the NTT implementation must satisfy.
Figure 2. Dashboard page for the theorem that shows the Rust function mlkem.ntt correctly implements the NTT specified in the NIST standard.

The specification clearly presents the theorem statement included in the Lean formal development: it separates the function input and preconditions from the post-condition by putting them above a horizontal line, and use fully qualified names with links to navigate to Rust and Lean definitions.

This feedback loop is especially useful for reviewing assumptions around intrinsics, target-specific code, and boundary conditions. A cryptographic developer can for example check whether the theorem fully captures what they expect their code to guarantee, and notice a formal statement is too weak, or a precondition is wrong.

The dashboards also aligns verification with continuous development. As Rust code changes, Lean models and proofs can be regenerated and replayed. When a proof breaks, that failure becomes a signal: either the implementation changed in a way that needs a proof update, or the change has exposed a real discrepancy with the specification.

This turns formal verification from a one-time research artifact into part of the engineering workflow.

Agentic proofs

The final ingredient is automation beyond traditional tactics: AI agents. Lean is well suited to this because proofs are machine-checked by a small trusted kernel. An agent may propose a proof script, but Lean independently verifies whether the proof is valid.

We use agents in two places. First, they help translate standards into Lean specifications. Because the resulting specification is executable, aligned to the original standard, tested against official vectors, supported by mathematical theorems, and much simpler than an implementation, it can be thoroughly audited even when an agent helped draft it.

Second, agents help write and maintain proofs. With the right libraries, tactics, examples, and documentation, agents can handle large amounts of proof work: unfolding generated models, applying specifications for helper functions, discharging arithmetic obligations, and repairing proofs after refactors.

This is particularly powerful because the Rust code and Lean proofs are separated. Agents do not need to annotate or modify the production Rust implementation to make a proof go through. They operate on the proof side, and the result is accepted only if Lean validates it and the final theorem states the desired guarantee without introducing unreviewed assumptions.

In practice, this changes the economics of verification. Work that previously required months of specialist effort can be accelerated dramatically. The proof engineer’s role shifts from writing every proof by hand to designing specifications, curating automation, reviewing theorem statements, and steering agents to complete their proofs.

Conclusion

Verified cryptography has often faced a difficult trade-off: the strongest guarantees came from specialized toolchains, generated code, and workflows that were hard for product teams to adopt. Rust, Lean, Aeneas, and agentic proof automation let us revisit that tradeoff.

By verifying Rust as written, deriving auditable specifications from standards, supporting optimized multi-architecture implementations, and reflecting proof results back to developers, formal verification can become part of normal cryptographic engineering rather than an after-the-fact research exercise.

That is the long-term promise: cryptographic code that remains fast, portable, maintainable, and developer-owned, while carrying machine-checked evidence that it implements the standards it is meant to realize.

Opens in a new tab

The post Verifying Rust cryptography in SymCrypt, from standards to code appeared first on Microsoft Research.

Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Developers are the reason behind best (and worst) parts of software development

1 Share

In the latest edition of our Developers Answer series, we spoke with developers about their best and worst projects, the reasons behind the challenges, and the ways they’ve made it across the finish line regardless of the hardships.

What’s the worst type of project?

Sure, the term “worst” means different things to different people, but most of the engineers agree: the worst types of projects are the ones that aren’t set up well by humans, not the ones that have big technical requirements. And, as you can probably guess, human-designed flaws are much more creative.

For example, one engineer highlighted that the worst project for him was working with family members who were not familiar with software development at all, affecting expectations, realisations and everything in between.

Another situation was a client project that took four months, with progress going smooth up until the last month. Nikola Buhiniček, Engineering Manager, said:

The client changed the scope significantly without much warning. We had to abandon a lot of the work we’d already done and come up with new features and opportunities. It felt like compressing two and a half months of work into one month before the deadline.

Another type of projects that drive engineers crazy are legacy code projects without real instructions on how to approach them, as explained by Edvin Teskeredžić, Senior AI Software Engineer:

We had this old legacy project, basically 10-year-old code which needed to be maintained. It was also all internal libraries but there was no documentation. The documentation simply lived in the heads of senior engineers, so you had to kind of figure it out yourself, and you couldn’t Google it because it was all internal libraries.

What’s the best, then?

In contrast, the best projects tend to be tied to others as well, only this time it’s about great teams and good colleagues. For some engineers, the best projects are those where leadership has a clear vision and that vision is clearly shown to the teams.

Other engineeers, such as Filip Bolčić, Full-Stack Engineer, highlighted teamwork as the best part of the projects they’ve worked on. For example:

Everybody’s insight was really valuable, and I had a great mentor from whom I learned a lot. It was just a really meaningful project for us, we felt really good working on it.

On the other hand, Nikola noted that sometimes, the best projects are the most challenging ones. He explained the process of adding the automations feature to Productive was one of the more challenging and interesting ones he worked on:

It was such a different feature from what we used to do, and I was given the opportunity to build it. In the end it was one of the best features we have. The usage is growing up from month to month. I’m really happy that it was something that I built like two years ago, and it’s still growing so fast.

It all comes down to the human factor

The hardest thing to fix as an engineer might not even be software-related, according to Hrvoje Rančić, Senior Software Engineer:

My posture. I had really terrible back pain from sitting too much during the COVID era. I think this is a very dangerous job because it involves a lot of sitting, and sitting is the new smoking, so I encourage everyone to move more if you’re a software engineer.

Ergonomics aside, Software Engineer Emin Mulaimović said that some of the biggest recent challenges he had were related to LLM’s:

We had this unstable LLM prompt which basically means you ask the same question and it gives different answers, and the answers are not marginally different. To fix that you just need to rephrase the question over and over and over again until it worked. And then when it worked you didn’t learn anything, you didn’t have fun fixing it, you’re just glad it’s over. Some bugs are fun to fix; this one wasn’t.

On the other end of the spectrum, the toughness of a task comes down to a personal mistake by the engineer themselves, and there’s rarely anything you can do but to fix it. That’s exactly what happened to Filip:

I remember working on a really big-scale production application and accidentally just deleting a lot of important stuff from the database. I was really young in my career and I remember being really stressed out about this, but the project manager protected me and we were able to restore a backup of this database, so in the end everything was good, but it was really stressful for me.

And to further drive the point home, sometimes the fix isn’t even tough, but so time-consuming you think you’re going crazy, as evidenced by Olga Koroleva, Engineering Manager:

I made a bug in the code where entries to the database were written wrong, and I had to manually fix thousands of them. After that, I always double-checked and triple-checked everything.

Make sure to check out the entirety of the video to find more about the best and worst projects, and the challenges of software development!

Special thanks to our engineering colleagues at Infobip, Nikola Buhiniček (Engineering Manager, Productive) and Filip Bolčić, (Full-Stack Engineer, Ars Futura).

The post Developers are the reason behind best (and worst) parts of software development appeared first on ShiftMag.

Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

The Frontend Verification Gap in AI-Assisted Development

1 Share

AI-assisted development has made frontend work feel much faster. A developer can ask for a form, a dashboard card, a table, a modal, or a responsive layout and get a decent first version almost immediately. The code may compile. The page may render. At first glance, the UI may look done.

But frontend developers know that “it looks done” and “it works well” aren’t the same thing.

A generated form might show validation errors visually but fail to announce them to a screen reader. A modal might open but not move focus to the right place. A dropdown might work perfectly with a mouse and still be unusable from a keyboard. A loading state might look fine in a demo but become confusing when the network is slow. A component might behave well with sample data and break as soon as real content is longer, missing, delayed, or unexpected.

That is the frontend verification gap in AI-assisted development. In this context, verification means checking whether an interface actually works properly for users under realistic conditions, not just whether the code compiles, the page renders, or the screen matches a design. It includes things like accessibility, keyboard behavior, focus management, state changes, loading and error handling, and whether someone can complete the intended task from start to finish. AI can help teams produce interface code faster than they can confidently answer those questions.

This isn’t an argument against AI tools. They can be genuinely useful. They can reduce repetitive work, help developers get unstuck, and speed up the first draft of a feature. But AI-generated frontend code should still be treated as a draft. The next challenge isn’t just generating UI code faster. It’s verifying that code with enough care.

Frontend correctness is harder than it looks

Some kinds of code are easier to verify than user interfaces. A function returns the expected value or it doesn’t. An API sends back the right response or it doesn’t. A script completes successfully or it fails.

Frontend work is different because the interface is where software meets people. A UI has to satisfy many expectations at once. It has to render correctly, respond to input, preserve state, support keyboard navigation, expose the right information to assistive technologies, and handle loading, errors, empty states, and unexpected data. It also has to fit the design system so the experience feels consistent.

AI tools are often good at producing the visible part of this work. They can generate a form, card, or table that looks reasonable in the default state. That’s helpful, especially when a developer needs a starting point.

The problem is that the default state is only one part of the experience. The harder questions come after the screen appears. Can someone complete the flow using only a keyboard? What happens when the request fails? Does focus move somewhere useful after an error? Are field labels and error messages connected correctly? Does the UI still make sense when there are no results? Is the generated code using existing design-system patterns, or did it quietly introduce a new one?

These aren’t small details. They are part of whether the interface actually works.

A quick review is not enough

A common AI-assisted workflow looks something like this: write a prompt, generate code, review the result, make a few edits, and move on. That may be fine for prototypes or internal experiments. It is much weaker for production frontend work.

The issue isn’t simply that AI makes mistakes. Developers make mistakes too. The issue is that AI can make incomplete work look surprisingly polished. The code may be clean. The structure may look familiar. The component may follow common framework conventions. That polish can make reviewers less likely to question the behavior.

Frontend problems are often missed this way. Accessibility issues, focus bugs, race conditions, missing empty states, and unclear error messages usually don’t jump out from a quick visual scan. They show up when someone interacts with the feature under less-than-perfect conditions.

AI-generated tests can create the same problem. A test may confirm that a component renders but not that a user can complete the task. Another test may check internal state changes while missing keyboard behavior, validation messages, loading states, or failure paths.

So the workflow needs to be stronger than “prompt, code, review.” Teams need better validation around AI-generated frontend work. That doesn’t have to mean a heavy process. It simply means being more intentional about what must be checked before a generated UI is considered ready.

Be clearer about what “done” means

One of the simplest ways to improve AI-generated frontend code is to give the tool clearer expectations before it starts writing code. Some of those expectations shouldn’t have to be repeated in every prompt. Rules such as using existing design-system components, following accessibility standards, preferring native HTML, and handling loading and error states can often be placed in a persistent project instruction file, such as CLAUDE.md, or another startup file that the agent reads at the beginning of its work. That gives the agent a shared baseline for the whole project and reduces the chance that important standards are forgotten from one task to the next.

A task-specific prompt can then focus on the details that are unique to the feature. For example, instead of simply asking for a form, the task might explain which fields are required, what should happen after submission, where focus should move after validation, and how the user should recover if the request fails.

The persistent instructions and the task-specific prompt serve different purposes. The first captures the team’s standing engineering expectations. The second explains what this particular feature needs to do.

This also makes review easier. The reviewer is no longer asking only whether the screen looks close to the mockup. They can check whether the feature follows the project’s established rules and whether the specific flow behaves as intended.

This matters because many frontend quality expectations are easy to leave unstated. Accessibility, focus behavior, loading states, and error recovery should be part of the agent’s working context wherever possible, rather than depending on a developer remembering to mention them in every prompt.

Let the design system do more work

AI tools are most useful when they operate inside clear boundaries. For frontend teams, one of the best boundaries is a strong component system.

If every generated feature creates its own buttons, inputs, modals, dropdowns, alerts, and tables, the team has to review the same concerns again and again. Is this button accessible? Does this modal manage focus correctly? Is this error message connected to the field? Does this dropdown support keyboard interaction? Are the styles consistent with the rest of the product?

That creates unnecessary rework. A stronger pattern is to put those decisions into reusable components. A button component should already handle variants, disabled states, focus styles, and accessible naming expectations. A modal component should already handle focus movement, escape behavior, labeling, and returning focus to the trigger. A form field component should already connect labels, helper text, required state, and validation messages. Then AI isn’t being asked to invent the pattern from scratch. It’s being asked to compose pieces that already carry the team’s standards.

There’s a big difference between prompting, “Build a modal form,” and prompting, “Use the existing Modal, TextField, Button, and FormMessage components to build this flow.” The second request gives the tool a safer path. It also gives the reviewer fewer things to worry about because the riskiest interaction patterns are already handled by shared components.

In that sense, a design system isn’t only about visual consistency. It can become a verification layer. It narrows the possible output and helps teams reduce the number of problems they need to catch manually.

Test the behavior users actually depend on

Automated checks will never catch everything. They can’t tell you whether a flow feels intuitive, replace a thoughtful review, or guarantee that every user will have a good experience. But they can catch common problems early, which makes them an important part of frontend verification.

Accessibility checks can flag missing labels, invalid ARIA usage, some landmark problems, and other frequent mistakes. Component tests can check state changes and validation behavior. End-to-end tests can confirm that someone can complete an important flow, while visual tests can catch certain layout regressions. The important thing is to test behavior, not just structure.

For example, a basic test might confirm that a form renders. A more useful test checks whether a user can enter values, trigger validation, understand the errors, correct them, submit the form, and receive clear success or failure feedback. Similarly, instead of checking only that a modal appears in the DOM, a test can confirm that focus moves into the modal, keyboard navigation works, the Escape key closes it, and focus returns to the original trigger.

This is where Playwright-style user-flow testing can be especially useful. It allows teams to test an interface in a way that is closer to how a person actually experiences it. The question becomes less about whether the interface renders and more about whether the user can complete the task.

AI can help generate these tests, but the team still has to define which behaviors matter. Asking an AI tool to “write tests for this component” leaves too much open to interpretation. A request to test keyboard navigation, validation errors, loading behavior, empty states, and failed submissions gives it a much clearer target. The quality of an AI-generated test still depends on the quality of the verification intent behind it.

Review the experience, not just the code

Code review still matters, but AI-assisted frontend work needs a slightly different review mindset. Reviewers need to look beyond whether the code is clean and whether the screen matches the expected layout. They should also ask: Are we using existing design-system components? Did the generated code introduce a custom control where native HTML would have been better? Are labels and errors connected correctly? Can the flow be completed with a keyboard? What happens when data is empty, delayed, or invalid? Do the tests cover real user behavior or mostly implementation details?

These questions help shift the review from syntax to experience. That doesn’t mean every pull request needs a long checklist. The process can still be lightweight. But the important concerns need to be visible somewhere. If accessibility, focus behavior, loading states, and error recovery never come up during review, they’ll continue to be missed.

AI doesn’t automatically solve that. In some cases, it makes the gap easier to miss because the generated result looks more complete than it really is.

Use AI without lowering the bar

The goal isn’t to make AI-assisted development feel risky or slow. The goal is to use AI for what it does well without letting it quietly lower the quality standard.

AI is useful for first drafts, repetitive scaffolding, alternate implementations, test ideas, and refactoring suggestions. It can help developers move through routine work faster. But it shouldn’t define what “good enough” means.

Frontend teams can get more value from AI when they pair it with clear engineering habits. Use existing components instead of generating new patterns each time. Include accessibility and interaction behavior in the prompt. Ask for loading, empty, error, and success states. Add automated checks for common problems. Test important flows the way a user would experience them. Review behavior, not just code structure.

These habits reduce rework. They also make AI-generated code easier to trust, because the trust comes from verification rather than from how confident or polished the generated output looks.

The frontend engineer’s role is shifting

AI-assisted development does not make frontend engineering less important. It changes where the value is. The value is not only in writing every line of UI code by hand. It’s in defining good component boundaries. It’s in knowing which patterns should be reused. It’s in understanding accessibility and interaction details. It’s in writing meaningful tests. It’s in noticing when a UI looks finished but isn’t actually ready.

That judgment matters because frontend failures are often experienced directly by users. A backend failure may return an error. A frontend failure may leave someone confused, stuck, or unable to complete a task. The user may not know whether they did something wrong, whether the application failed, or whether the interface was never designed for their way of navigating. Good verification protects users from that confusion.

Closing the gap

AI is making frontend development faster. That’s a real benefit. But faster code generation doesn’t automatically create better interfaces. In many teams, the bottleneck will move from writing code to checking whether the code behaves well.

The teams that benefit most from AI-assisted development won’t be the ones that generate the most UI code. They’ll be the ones that build strong feedback loops around that code.

For frontend teams, that means treating verification as part of development from the start. Component contracts, design-system guardrails, accessibility checks, user-flow tests, and behavior-focused reviews aren’t extra polish. They’re how teams keep quality high while still using AI productively.

The future of AI-assisted frontend development is not just better prompting. It is better verification.

The views expressed are my own and do not represent those of my employer. 

AI use acknowledgment

AI assistance was used lightly for phrasing, editing, and tightening parts of this draft. The article’s ideas, structure, examples, and final review are my own.



Read the whole story
alvinashcraft
36 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories