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

Build for iPhone Duo with new resources

1 Share
Front and rear views of iPhone Duo shown side by side in Star White and Night Sky, displaying desert landscape wallpapers in both light and dark mode.

Get Xcode 27.1 beta.

View downloads and release notes

Get Figma and Sketch design kits for iPhone Duo from the Apple Design Resources.

Download design kits from the Apple Design Resources

Sign up for workshops around the world to learn how to optimize your app and build customized experiences for all screen sizes.

Explore workshops

Find out more about preparing your app for iPhone Duo.

Learn more about iPhone Duo

Read the whole story
alvinashcraft
12 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Claude couldn’t hack OpenAI. Then Anthropic shipped Opus 5.

1 Share
Abstract door

Three security researchers at Hacktron AI found a memory-corruption bug in a widely used image library. Finding it was the easy part.

The hard part was turning it into something that works on a real server, so on July 24 they handed that job to Anthropic’s Claude Opus 4.8. The model managed it only with the operating system’s memory randomization switched off. With the protection on — this is the way every production box runs it — nothing it wrote held up.

That evening, Anthropic released Opus 5.

The researchers came back the next morning with the same bug and the new model. Roughly three hours later, Opus 5 had a working ARM64 exploit running against a Mac on their desk. About four hours after that, they had remote code execution against a test forum.

Less than 72 hours after they started, they were reading from OpenAI’s private monorepo — using an OpenAI employee’s Codex account to open a pull request against a README, then stopping there.

Hacktron AI published its account of the incident on its website this week.

It started with an image

The bug wasn’t in anything OpenAI wrote. Hacktron was testing community.openai.com, the company’s user forum, which runs on Discourse — the same off-the-shelf forum software behind thousands of other sites.

Discourse normally screens uploaded images with FastImage. But FastImage doesn’t handle HEIC and HEIF, so those files get passed to ImageMagick instead, and ImageMagick decodes them with libheif. The version running in the Debian 12 base image the forum used, 1.19.7, had a heap buffer overflow that a specially crafted file could trigger.

A fix had landed upstream the previous year. But the commit wasn’t documented as a security fix and never got a CVE, so it never triggered a backport into the Debian package the forum was using — a patched bug that stayed exploitable because nobody labeled it.

The researchers adapted the exploit for the x86-64 and jemalloc configuration Discourse runs, and a malformed HEIC image was enough to trigger remote code execution.

Discourse later confirmed the vulnerability in security advisory GHSA-vhm9-85gw-x335, rating the upstream libheif flaw — tracked as CVE-2026-32882 — 8.8 out of 10 on the CVSS severity scale. The New Stack has reached out to Hacktron AI for additional details about the researchers’ use of Claude and will update this story if we hear back.

One exploit, a much larger path

Code execution on a forum is a bad day for the forum. But it shouldn’t be a bad day for the company that owns the forum. This is where the chain crossed into something that was OpenAI’s own. Hacktron then found a flaw in OpenAI’s single sign-on system: sign-in tokens issued for the forum carried excessive permissions, granting full API access to the linked ChatGPT and Codex accounts. Some of those accounts belonged to OpenAI employees.

One employee’s Codex account was connected to OpenAI’s GitHub environment, opening a path to the company’s private repositories. Hacktron says other accounts could have exposed connected services including Slack and email.

The team stopped there. Using Codex, they made a harmless documentation change against OpenAI’s private openai/openai monorepo and opened a pull request — enough to prove the access was real, and nothing more. Hacktron’s write-up says the pull request’s details were redacted at OpenAI’s request.

From assistant to exploit developer

Up to this point, those three experienced researchers were still in the loop. So Hacktron ran the experiment again with the humans mostly out of it.

They put Claude in an autonomous agent loop — giving it a goal, a target, and time to keep working — pointed at a Discourse instance of their own. The model got there on its own, achieving remote code execution and demonstrating it by reading /etc/hosts from inside the container.

Getting it started took one piece of misdirection: Opus refused to write an exploit aimed at a live remote host. So the team proxied their own instance through rce.ee/ctf-forum, a URL that made the target look like it was part of a capture-the-flag exercise.

Memory-corruption exploitation has always been specialist work, invovling memory layouts, allocators, operating system internals, and protections to make all of it wildly unreliable. Hacktron’s run signals a meaningful share of that work might be able to be delegated to AI now. It also suggests the line between security research and attack development is — from the model’s side, anyway — partly a question of what you consider a target.

The full chain

Put together, the attack looked like this:

HEIF upload → libheif overflow → code execution on the forum → over-permissioned SSO tokens → employee ChatGPT/Codex account → connected GitHub → pull request in openai/openai

A two-month project, under $3,000

The OpenAI intrusion was one thread in a broader project the team called “HEIF Heist,” a roughly two-month sweep of image-processing infrastructure across multiple major technology platforms. The whole effort consumed less than $3,000 in model tokens.

OpenAI paid Hacktron a $6,500 bounty for the account-takeover flaw on its side. It has since narrowed the permissions on community sign-in tokens and revoked the affected tokens and sessions.

The post Claude couldn’t hack OpenAI. Then Anthropic shipped Opus 5. appeared first on The New Stack.

Read the whole story
alvinashcraft
23 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

It's only a decade late, I'll learn TypeScript!

1 Share

I've been a bit worried lately. Of course, my friends will tell you that's my norm. But more specifically, I've been worried about my coding skills. As I use AI to generate more and more code, and it does that job well, I kinda miss actually writing code. I've also had a bit of writer's block in terms of topics to cover, and especially in the area of the web platform. A few days ago it occurred to me - I've never really dug into TypeScript. Sure, I've used it, many times, in the fourteen or so years it's been out, but many times I kinda guess at what I'm doing and it was never my default for projects. Partly that's due to my philosophy of avoiding a build step in my projects and just KISS in general.

But as I said - the inspiration hit me this past week and I thought I'd do some digging. Here's a few initial thoughts and experiments. Oh, and I still used AI a bit and I'll explain exactly how in the post below.

TypeScript via Vite

Working with TypeScript can be as simple as npm i -g typescript and then tsc somefile.ts. I decided to use Vite instead as it's focused on web projects and most of my development involves web projects. I assume most of my readers know of Vite, but if you somehow have managed to avoid hearing about it or using it yet, it is a lightning quick build tool for web projects. By "lightning quick" I mean that I still remember the first time I used it thinking it had failed to actually do anything - that's how quick it works.

I also appreciated the fact that Vite's CLI supports a vanilla-ts scaffold that is really lightweight. On the other hand, I'm not necessarily a fan of what it actually shows as it feels a bit like bad practice, specifically an index file with just one div:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + TS</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

And then injects a bunch of HTML in main.ts:

import './style.css'
import heroImg from './assets/hero.png'
import typescriptLogo from './assets/typescript.svg'
import viteLogo from './assets/vite.svg'
import { setupCounter } from './counter.ts'

document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
<section id="center">
  <div class="hero">
    <img src="${heroImg}" class="base" width="170" height="179">
    <img src="${typescriptLogo}" class="framework" alt="TypeScript logo"/>
    <img src="${viteLogo}" class="vite" alt="Vite logo" />
  </div>
  <div>
    <h1>Get started</h1>
    <p>Edit <code>src/main.ts</code> and save to test <code>HMR</code></p>
  </div>
  <button id="counter" type="button" class="counter"></button>
</section>

<div class="ticks"></div>

<section id="next-steps">
  <div id="docs">
    <svg class="icon" role="presentation" aria-hidden="true"><use href="/icons.svg#documentation-icon"></use></svg>
    <h2>Documentation</h2>
    <p>Your questions, answered</p>
    <ul>
      <li>
        <a href="https://vite.dev/" target="_blank">
          <img class="logo" src="${viteLogo}" alt="" />
          Explore Vite
        </a>
      </li>
      <li>
        <a href="https://www.typescriptlang.org" target="_blank">
          <img class="button-icon" src="${typescriptLogo}" alt="">
          Learn more
        </a>
      </li>
    </ul>
  </div>
  <div id="social">
    <svg class="icon" role="presentation" aria-hidden="true"><use href="/icons.svg#social-icon"></use></svg>
    <h2>Connect with us</h2>
    <p>Join the Vite community</p>
    <ul>
      <li><a href="https://github.com/vitejs/vite" target="_blank"><svg class="button-icon" role="presentation" aria-hidden="true"><use href="/icons.svg#github-icon"></use></svg>GitHub</a></li>
      <li><a href="https://chat.vite.dev/" target="_blank"><svg class="button-icon" role="presentation" aria-hidden="true"><use href="/icons.svg#discord-icon"></use></svg>Discord</a></li>
      <li><a href="https://x.com/vite_js" target="_blank"><svg class="button-icon" role="presentation" aria-hidden="true"><use href="/icons.svg#x-icon"></use></svg>X.com</a></li>
      <li><a href="https://bsky.app/profile/vite.dev" target="_blank"><svg class="button-icon" role="presentation" aria-hidden="true"><use href="/icons.svg#bluesky-icon"></use></svg>Bluesky</a></li>
    </ul>
  </div>
</section>

<div class="ticks"></div>
<section id="spacer"></section>
`

setupCounter(document.querySelector<HTMLButtonElement>('#counter')!)

I'm probably being real nit picky here, but having 99% of the HTML for something in JavaScript versus HTML that gets enhanced feels off to me. But again - I'm being picky, and the actual code spit out is pretty minimal. (I could go on for ages about what's "appropriate" for scaffolded projects and such. Give me a soap box and I'll tell you more.)

So yeah, that's how I started. I pretty much nuked most of the code in main.ts and simplified even more.

I mentioned earlier I still used a bit of AI in my exploration, and one example was to help clarify things a bit. For example, my very high level basic understanding of TypeScript basically came down to type annotations like so:

let name:string = "Ray";

This line in the sample code confused me:

document.querySelector<HTMLDivElement>('#app')!.innerHTML = `

So I simply asked Claude to explain it:

The <HTMLDivElement> (generic type argument)

By default, querySelector returns Element | null. Element is a very generic type — 
TypeScript doesn't know whether it's a div, an input, an SVG circle, etc. That 
matters because different elements have different properties: Element doesn't 
have .value, for example, but HTMLInputElement does.

Writing querySelector<HTMLDivElement>('#app') tells TypeScript: "trust me, the 
thing this selector finds is a div." So the return type becomes 
HTMLDivElement | null instead of Element | null, and you get proper autocomplete 
and type checking for div-specific properties.

Like the !, this is a compile-time-only assertion — TypeScript doesn't verify 
it. If #app were actually a <span>, the code would still compile and run; you'd 
just have a lying type.

Fun fact: in this particular line the generic is technically unnecessary, since 
.innerHTML exists on Element anyway. It's there mostly out of habit/convention 
(this looks like the Vite starter template). It becomes genuinely useful when 
you need element-specific properties like .value, .src, or .checked.

Cool. And this is something I've really liked about AI in the past. I feel like my question, a particular syntax being used, wouldn't Google well and AI tools do a better job of getting to the right answer.

At this point, I felt ready to write something.

Actually, let's not build for the web...

So when I figured out a good simple thing to build, I realized I didn't actually need a web UI for it, at least not initially, so I did a quick pivot and looked up info on vitest, a "Vite-native" testing framework. I actually don't have a huge amount of experience with unit tests as so much of my work is educational, demo based, and such, that tests aren't really required. That being said, I've written them, I'm familiar with Jest (which vitest is compatible with), so it seemed like an easy win.

I did a quick npm i -D vitest and added this to my package.json scripts block: "test": "vitest"

This let me run npm run test in my terminal and get immediate feedback on what I was writing.

Roll the dice...

About five years ago, I thought it would be fun, and surely pretty simple, to build an RPG in Vue.js (here's the first entry: "Testing Vue.js Application Files That Aren't Components". I never finished that project, but I don't care - it was fun to work on.

As part of that project, I built a simple "dice" utility that supported typical D&D dice rolls of the form: xDy. This translates to roll a Y-sided dice X times. It can get fancy too, so 2d6+2 means roll a six sided die two times and then add two tp the final result. (You could imagine a scenario where a player is attempting to do something they are talented at, so the +2 represents a bonus to their chance to do the thing.)

The code for that looked like so:

export const dice = {

	roll(style) {
		let bonus=0, total=0;
		if(style.indexOf('+') > -1) {
			[style, bonus] = style.split('+');
		} 
		
		let [rolls, sided] = style.split('d');
		
		//console.log(rolls, sided);
		for(let i=0;i<rolls;i++) {
			total += getRandomIntInclusive(1, sided);
		}
		total += parseInt(bonus);
		return total;
	}
}

function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is inclusive and the minimum is inclusive 
}

I thought a good first test would be to attempt to rewrite this in a TypeScript file. I actually broke it out into two files, one for the roll action and a 'util' one for the random number selection.

I'll share the utility function first as it was the simplest change:

export function getRandomInt(min: number, max: number): number {
  min = Math.ceil(min)
  max = Math.floor(max)
  return Math.floor(Math.random() * (max - min + 1)) + min
}

This was really simple - just adding types to my arguments and result. I also removed inclusive from the name as I never see myself needing a non-inclusive random number selection.

The test for this was pretty simple too:

import { describe, it, expect } from 'vitest'
import { getRandomInt } from './utils'

describe('getRandomInt', () => {
  it('returns a number between min and max', () => {
    const min = 1;
    const max = 2;
    const randomInt = getRandomInt(min, max);
    expect(randomInt).toBeGreaterThanOrEqual(min);
    expect(randomInt).toBeLessThanOrEqual(max);
  })

  it('returns an integer', () => {
    const randomInt = getRandomInt(1, 10);
    expect(Number.isInteger(randomInt)).toBe(true);
  })
});

I discovered after writing this that you can actually screw with Math.random() which is cool af as the kids say, so I may return to this. But now let's turn to the dice stuff which got REALLY complicated, but in a fun way.

My first version went a bit like this:

import { getRandomInt } from './utils.ts';

export const dice = {

	/*
	xdy

	x rolls of a y sided dice

	xdy+n
	xdy

	x rolls of a y side dice + n
	*/
	roll(styleInput: string): number {
		let bonus:number=0, total:number=0;
        let bonusStr:string='';
        let style = '';

        if(styleInput.indexOf('+') > -1) {
			[style, bonusStr] = styleInput.split('+');
			bonus = Number(bonusStr);
		} else style = styleInput;
		
		const [rollsStr, sidedStr] = style.split('d');
		const rolls = Number(rollsStr);
		const sided = Number(sidedStr);

		//console.log(rolls, sided);
		for(let i=0;i<rolls;i++) {
			total += getRandomInt(1, sided);
		}

		total += Number(bonus);
		return total;
	}
}

And you'll notice it's a bit longer than the original version, and I don't mean more characters, I expected that, but some things took me a bit by surprise. Take this (original) line:

let [rolls, sided] = style.split('d');

My original code just coerced rolls into a number, which is bad, but I tried this and it didn't work:

let [rolls:number, sided: number] = style.split('d');

Which is obvious - the result of the string split function is strings. What didn't occur to me at the time was to chain to map:

let [rolls, sided] = s.split('d').map(Number);

Live and learn, right? I built some basic tests for that and was curious what else I could do. I turned to my AI agent (Claude in this case) and just asked what other 'dice format' options were available, and was pleasantly surprised to discover quite a few. In particular I thought these made sense:

  • Keep highest, lowest, using KH or KL. So for example, 3D6KH1 means roll a six sided die three times, keep the highest one.
  • Given a dice string input, what's the lowest value, and what's the highest value?

At this point, I decided to let AI help me write a bit of the code and handle updating the library to be a bit more flexible. The final result supports all of the above:

import { getRandomInt } from './utils.ts';

const VALID_ROLL_REGEX =
    /^(\d*)d(\d+)(?:(kh|kl|dh|dl)(\d+))?([+-]\d+)?$/i;

function validRoll(style: string): boolean {
    return VALID_ROLL_REGEX.test(style);
}

function parseRoll(styleInput: string) {
    if (!validRoll(styleInput)) {
        throw new Error(`Invalid dice roll style: ${styleInput}`);
    }

    const match = styleInput.match(VALID_ROLL_REGEX)!;

    return {
        rolls: match[1] ? Number(match[1]) : 1,
        sided: Number(match[2]),
        keepMode: match[3]?.toLowerCase(),
        keepCount: match[4] ? Number(match[4]) : 0,
        bonus: match[5] ? Number(match[5]) : 0,
    };
}

function applyKeepDrop(
    dice: number[],
    keepMode: string | undefined,
    keepCount: number,
): number[] {
    if (!keepMode || keepCount === 0) {
        return dice;
    }

    dice.sort((a, b) => a - b);

    switch (keepMode) {
        case 'kh':
            return dice.slice(-keepCount);
        case 'kl':
            return dice.slice(0, keepCount);
        case 'dh':
            return dice.slice(0, -keepCount);
        case 'dl':
            return dice.slice(keepCount);
        default:
            return dice;
    }
}

export function roll(styleInput: string): number {
    const { rolls, sided, keepMode, keepCount, bonus } =
        parseRoll(styleInput);

    const dice: number[] = [];

    for (let i = 0; i < rolls; i++) {
        dice.push(getRandomInt(1, sided));
    }

    const total = applyKeepDrop(dice, keepMode, keepCount)
        .reduce((sum, die) => sum + die, 0);

    return total + bonus;
}

export function rollWithAdvantage(styleInput: string): number {
    return Math.max(roll(styleInput), roll(styleInput));
}

export function rollWithDisadvantage(styleInput: string): number {
    return Math.min(roll(styleInput), roll(styleInput));
}

export function minRoll(styleInput: string): number {
    const { rolls, keepMode, keepCount, bonus } = parseRoll(styleInput);
    const dice = Array(rolls).fill(1);

    return applyKeepDrop(dice, keepMode, keepCount)
        .reduce((sum, die) => sum + die, 0) + bonus;
}

export function maxRoll(styleInput: string): number {
    const { rolls, sided, keepMode, keepCount, bonus } =
        parseRoll(styleInput);
    const dice = Array(rolls).fill(sided);

    return applyKeepDrop(dice, keepMode, keepCount)
        .reduce((sum, die) => sum + die, 0) + bonus;
}

I won't share the entire test file (don't worry, I'll link to the repo in a bit), but as I mentioned above, you can actually "hack" random and that's really freaking sweet. For example, here is a test for rolls where you drop the lowest value:

it('handles drop lowest', () => {
    const spy = vi.spyOn(Math, 'random')
        .mockReturnValueOnce(0.1)  // first roll → low
        .mockReturnValueOnce(0.9);  // second roll → high

    const result = roll('2d6dl1');
    expect(result).toBe(6);

    spy.mockRestore();
});

Looking at it just now I see the second mock isn't needed - I'll fix that (eventually, honest) - but you get the idea.

The bits, and nothing but the bits...

So this isn't a "real" project, but I loved getting my feet wet in TypeScript and - tbh - just writing some darn code. The repo is here: https://github.com/cfjedimaster/typescript-stuff/tree/main/vite-one

I've already got an idea for a next project, one that will actually have a web presence, so I'll share that when I get the time.

And this is where you come in. I'd be willing to bet 99% of you are writing TypeScript daily and have plenty of comments - so let me know. Just be gentle. Please.

Read the whole story
alvinashcraft
37 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

What Does the System Need to Know Before It Builds Anything?

1 Share
Adhithi Ravichandran Speaking about AI-assisted engineering and the journey from prompt to production at KCDC 2026.

AI-assisted development is changing the way we build software. We can now generate code, write tests, refactor applications, create UI, and even get suggestions for architecture much faster than before. But the more I use AI in real engineering workflows, the more I realize that generating code is becoming the easy part.

The harder question is this:

What does the system need to know before it builds anything?

This was one of the main ideas behind my recent KCDC talk, From Prompt to Production. The talk explored what it really takes to move from an AI-generated idea or design to code that is actually usable in an existing production application.

The biggest lesson was simple: AI can generate code very quickly, but if it does not understand the system around that code, the result may look correct while still being wrong for the application.

Generating Code Is No Longer the Hard Part

A few years ago, building a new feature usually meant spending a significant amount of time understanding the requirements, reviewing the design, finding the right parts of the codebase, writing the implementation, testing it, and reviewing everything before it could move toward production.

AI can now accelerate a large part of that process.

A coding agent can generate a React component, create tests, explain unfamiliar code, refactor an existing feature, or help wire up an API. In design-to-code workflows, it can even take information from a design tool and produce an initial implementation.

That is a major productivity improvement.

But it also changes where the difficult part of engineering lives.

The challenge is no longer simply, “Can AI generate this code?”

In many cases, the answer is yes.

The more important question becomes,

“Will the generated code actually fit the system we already have?”

A Screen Can Look Right and Still Be Wrong

This became very clear while working on AI-assisted design-to-code workflows.

Imagine giving an AI tool a Figma design and asking it to build the screen in React. The output may look very close to the design. The spacing is correct, the layout looks good, the button is in the right place, and the screen renders without errors.

At first glance, that looks like success.

But when you inspect the implementation, you may find that the AI created a new button instead of using the existing Button component. It may have hardcoded a color instead of using a design token. It may have created a new form pattern even though the application already has a standard approach. It may place files in the wrong folder or bypass an abstraction the team uses everywhere else.

The UI still works.

The code is still valid.

But it does not belong in the application.

This is an important distinction because production software is not built in isolation. Most applications already contain years of decisions, conventions, components, types, APIs, architecture patterns, and business rules.

AI does not automatically understand any of that.

If we do not provide the right context, it has to guess.

The Real Problem Is Context

The first reaction to poor AI-generated code is often to improve the prompt.

We tell the AI to use the existing Button component. We tell it not to hardcode values. We tell it which API to use, where files should go, and which patterns to follow.

That works for a while.

But eventually, you realize that you are trying to explain the entire application and engineering organization inside every prompt.

That is not really a prompt engineering problem.

It is a context problem.

For AI to generate useful code, it needs to understand several different types of context.

Design Context

The AI needs to understand what the interface is supposed to represent. That includes layout, components, variables, spacing, responsive behavior, and design intent.

A screenshot can show what something looks like, but it does not necessarily explain what the elements mean.

Structured design information can provide much richer context.

Component Context

The AI also needs to know what already exists.

Which components should be reused? What props do those components accept? What variants are available? Which components are part of the official design system?

Without that information, the model may create something new even when the application already contains the correct solution.

Engineering Context

Finally, the AI needs to understand how the application itself is built.

Where should this feature live? How does the application access data? What types already exist? What architectural patterns does the team follow? Which utilities or services should be reused?

The more of this information the agent understands, the less it has to guess.

Design Systems Are Becoming AI Infrastructure

Design systems have always been useful for consistency.

They help designers and developers share a common language and avoid rebuilding the same UI patterns over and over again.

AI gives design systems another role.

They become part of the context that helps the AI understand how the organization builds software.

If a Button in Figma clearly maps to an existing production Button component, there is less ambiguity. If the design uses defined tokens instead of arbitrary colors and spacing values, the generated implementation has a better chance of matching the application.

Tools like Storybook can also become useful sources of context because they show which components actually exist and how they are used.

This means practices that were already good engineering practices become even more valuable in an AI-assisted environment.

Clear naming matters more. Reusable components matter more. Consistent APIs matter more. Good documentation matters more.

A well-structured system reduces ambiguity for developers, designers, and AI agents.

Designers Are Part of the AI Workflow Too

One of the most interesting things we learned was that AI-assisted design-to-code is not really just a developer workflow.

Designers and developers have to work through it together.

Traditionally, a designer creates the interface and a developer interprets that design in code. With AI-assisted workflows, there is now another consumer of that design: the coding agent.

That does not mean designers need to become AI engineers.

But it does mean that the structure of the design matters downstream.

If designs use real components, meaningful names, variables, clear layout structures, and intentional grouping, the AI receives much better information. If the design is visually correct but structurally inconsistent, the agent gets a weaker representation of the design intent.

This is one reason I think AI is pushing design and engineering teams toward closer collaboration.

Many decisions that were previously understood informally now need to be made more explicit.

The Same Problem Exists in the Codebase

Engineering teams have a similar challenge.

Large applications often contain multiple ways of solving the same problem. Different teams may have introduced different patterns over time. Some architectural decisions are well documented, while others live mainly in the heads of senior engineers.

An experienced developer learns those patterns gradually.

An AI agent does not have that history.

If the repository contains five different examples of how to solve the same problem, the agent may choose any of them.

This can expose inconsistencies that have existed in the codebase for years.

And because AI can generate code very quickly, it can also reproduce those inconsistencies very quickly.

That is one of the reasons I often say:

AI does not automatically eliminate your inconsistencies. It can automate them.

The better organized and documented the system is, the more useful AI becomes inside it.

Better Context Does Not Mean More Context

There is another important part of this conversation.

If context is valuable, it is easy to assume that we should give AI as much information as possible.

That is not always the right approach.

More context can also introduce noise.

An agent working on a small feature does not need to understand the entire repository. It needs the information that is relevant to the task.

This means we also have to think carefully about how context is provided.

Which design frame does the agent need? Which files matter? Which components are relevant? Which business rules apply? Which architectural rules should be persistent across the project?

This is where the conversation starts to move beyond simple prompt engineering.

We are not just writing better instructions.

We are designing the environment in which the AI operates.

Every Repeated Prompt Is a Signal

One thing I now pay attention to is how often I repeat the same instruction to an AI tool.

If I constantly have to say, “Use our existing components,” that probably means the agent does not have enough component context.

If I repeatedly tell it not to call an API directly from a UI component, that may indicate that the architectural rule should be documented somewhere the agent can access consistently.

If every prompt includes an explanation of the repository structure, that information may belong in persistent project instructions instead.

Repeated prompts are often a signal that something should be encoded into the system.

The more stable rules we make discoverable, the less work we have to repeat manually.

Validation Still Matters

Even with strong context, AI-generated code should not be treated as finished code.

Context reduces guessing, but it does not guarantee correctness.

The code still needs to compile. Tests still need to pass. The interface still needs to match the intended experience. Accessibility, security, performance, and architecture still matter.

And human review still matters.

I like to think of AI-generated code as a hypothesis.

The model is essentially saying, “Based on the information you gave me, this is what I think the implementation should be.”

The rest of the engineering process has to validate that hypothesis.

That is why automated testing, architecture checks, visual validation, and human review become even more important as generation gets faster.

The Engineer’s Role Is Shifting

I do not think the most interesting story about AI and software engineering is simply that engineers will write less code.

We probably will write less code manually.

But there is still a lot of engineering around that code.

Someone has to understand the problem. Someone has to decide what context matters. Someone has to define the constraints. Someone has to evaluate trade-offs. Someone has to recognize when the generated solution technically works but does not actually belong in the system.

The implementation step is getting compressed.

The thinking around the implementation is becoming more valuable.

That is why the question I keep coming back to is not:

How much code can AI generate?

It is:

What does the system need to know before it generates anything?

The teams that answer that question well will get much more value from AI than the teams that simply focus on better prompts.

Because once the context is reliable, generating the code becomes the easy part.

I spend a lot of my consulting work helping teams answer this exact question: how do we make AI useful inside the systems we already have?

That often means improving the context around the model, design systems, codebases, architecture, workflows, validation, and the way design and engineering work together.

If your team is exploring AI-assisted development and wants to move from experimentation to something reliable in production, you can learn more about my work at Surya Consulting:

Surya Consulting: Modern Engineering Systems, Powered by AI

Read the whole story
alvinashcraft
45 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

ESLint v10.11.0 released

1 Share

Highlights

Faster startup and linting

This release includes a set of performance improvements across ESLint’s core linting pipeline and the stylish formatter. Linting results and the public API are unchanged: the same files produce the same messages as before, just faster.

The main improvements are:

  • Startup. Loading the eslint package no longer eagerly initializes the JSON Schema validator used for rule options. It’s now created the first time a rule’s options actually need to be validated, which removes about 45 modules from the initial dependency graph and reduces package load time by 20–25%.
  • Rule execution. When ESLint traverses a file’s structure, it has to decide, for every piece of code, which rules are interested in it and then call those rules. This is the most frequently executed part of the linting process, and it now takes a shorter route in the most common cases. Common node selector types are handled by fast paths, and rule visitors are invoked directly whenever possible instead of through a wrapper function on every node.
  • Overhead per reported problem. When a rule reports a problem, ESLint needs to perform several processing steps before displaying a message. Some of those steps have been made cheaper: placeholders in message templates are only filled in when necessary, autofixes are checked for validity without serializing each one to JSON, and one fixer object is shared by all problems reported in a file instead of creating one per fix. Also, files without any eslint-enable or eslint-disable comments now skip the directive processing step altogether.
  • Default formatter. The default stylish formatter avoids stripping terminal control characters from strings that don’t contain any and no longer runs a regular expression on every message, which speeds up printing large numbers of problems.

Actual results will vary depending on your configuration and hardware, but projects using many core rules should see a noticeable improvement in both startup time and overall linting time.

Features

Bug Fixes

Documentation

Chores

  • 520dd77 perf: Implement fast paths in critical areas (#21210) (Nicholas C. Zakas)
  • 92086c8 test: update EMFILE error generation for Node.js 26.9.0 compatibility (#21330) (Francesco Trotta)
  • 9ac7eb6 chore: update github/codeql-action action to v4.38.0 (#21331) (renovate[bot])
  • 24310e3 chore: update ecosystem plugins (#21324) (ESLint Bot)
  • 45ad79e ci: bump pnpm/action-setup from 6.0.10 to 6.1.0 (#21318) (dependabot[bot])
  • ac74e37 chore: Add AGENTS.md with AI disclosure requirements (#21221) (Nicholas C. Zakas)
  • c832660 chore: Upgrade Stylelint to the latest version in docs (#21245) (Jung Hyeon Jun)
  • f9f88fc chore: update ecosystem plugins (#21308) (ESLint Bot)
  • fc81076 ci: add more types integration tests (#20395) (Nitin Kumar)
Read the whole story
alvinashcraft
58 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

AI Can Build Your UI. Now Figma Can Tell It When It Screwed Up.

1 Share
AI can now build a UI in seconds, but that doesn't mean it built the UI you actually designed. A new Applitools integration turns Figma designs into visual baselines, automatically catching the subtle differences AI coding agents leave behind before they make it into production.
Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories