Web Development ·28 May 2024 ·12 min

Generative AI Developer Trends: What Changed in Building Web Apps

Generative AI cut the cost of writing code and left the cost of proving it correct untouched. That asymmetry explains the shift in code review, testing, junior onboarding and estimates on web application teams.

Pranav Begade By Pranav Begade
Generative AI Developer Trends: What Changed in Building Web Apps

Generative AI changed the cost of writing code and left the cost of being sure the code is correct exactly where it was. Nearly every trend worth naming in web development over the last two years falls out of that one asymmetry.

Typing was never the slow part of shipping a web application. Deciding what the thing should do, agreeing on it, wiring it into a system that already exists, and proving it did not break anything: those were the slow parts. A tool that writes a React component in four seconds compresses one step of a pipeline whose other steps did not move. Plan around that honestly and you get a real speedup. Plan around the demo and you get a backlog of code nobody fully understands.

This is a working note on what actually shifted, for a developer who already uses these tools daily and a team lead deciding how far to push adoption. Two things it is not. It is not a tooling roundup: for that inside a specific stack, see AI tooling in a MERN codebase. And it is not a measurement framework, since measuring the return on AI coding tools is its own problem.

Where it speeds you up, and where it quietly costs you

The reliable predictor of whether generation helps on a given task is not how hard the task is. It is how long it takes you to know the output is wrong. Sort your work by that and the pattern is obvious within a week.

TaskHow fast you find out it is wrongNet effect
Form, table and list components against an existing design systemInstantly, you look at itLarge gain
Type definitions and validation schemas from a sample payloadThe compiler tells youLarge gain
One-off scripts, data backfills, CSV munging, log parsingYou run it on a copyLarge gain
Tests written against an implementation you already trustThey pass or they failSolid gain, with a caveat below
Explaining unfamiliar code, a regex, a query plan, a legacy moduleYou verify the claim against the codeSolid gain
Authorisation rules, ownership checks, tenant scopingPossibly never, until a customer sees another customer's dataNet loss without full review
Money: pricing, proration, refunds, ledger entriesAt reconciliation, weeks laterNet loss without full review
Concurrency, retries, idempotency, cache invalidationUnder load, in production, intermittentlyNet loss without full review

Notice that the top rows are not the easy tasks and the bottom rows are not the hard ones. A correct proration rule is not intellectually harder than a good data grid. The difference is that the grid is self-evident and the proration rule is not. Generation multiplies whatever feedback loop you already have. Where there is no loop, it multiplies nothing and adds volume.

The second real gain is quieter and rarely measured: reading. Pointing a model at a file you did not write and asking what calls it, what the nullable fields imply, and what happens if this promise rejects is faster than tracing it yourself, and the answer is checkable. On a legacy codebase that is often worth more than any generation.

Now the other direction. These are the failure modes that make generation a net loss, and they are worth naming precisely, because a failure mode with a name is something you can review for.

Confident wrong APIs

A method that does not exist, or did exist in an older major version of the library. TypeScript catches some of it. Anything dynamic, anything configured by an options object, anything where a valid-looking string key is silently ignored, none of that gets caught. Config objects for bundlers, ORMs and cloud SDKs are the usual site.

Plausible surface, wrong semantics

The code compiles, passes the happy path, and encodes a subtly different rule than the one you asked for. An off-by-one on an inclusive date range. A soft delete that filters in one query and not in the sibling one. A permission check that verifies the user is authenticated but not that the record belongs to them. This is the expensive category and it is invisible at a glance.

Silent over-engineering

Ask for a function and get a class, a factory, an options interface, a retry wrapper and a try/catch that swallows the error and returns null. Every layer is defensible alone. Together they add code you own forever, and the swallowed error will cost someone an afternoon in six months.

Convention drift

The model cannot see the unwritten rules your codebase runs on: which error class to throw, whether dates are stored as UTC strings or timestamps, which layer may talk to the database, that you always paginate. Each generated file drifts a little. Nothing breaks. A year later there are three ways of doing everything and nobody decided that.

The steering spiral

Two prompts in, the output is nearly right. Five prompts in, you are describing the solution in English at a level of detail that would have been faster to write in code, and you have lost the thread of your own design. A two-attempt rule helps: if the second try has not converged, close the panel and write it.

Debugging by plausible hypothesis

Given a stack trace and no reproduction, a model returns a confident and specific cause. It is sometimes right. When it is wrong, it is wrong in a way that reads exactly like being right, and you can lose an hour testing a theory you would never have formed. Use it to cluster errors and propose candidates. Do not let it conclude.

Code review is now the bottleneck, and it needs a different shape

Review used to run on an assumption nobody wrote down: that the author had already thought about every line, because typing it forced them to. That assumption is gone. A four hundred line diff can now represent ten minutes of human attention, and the reviewer cannot tell from the diff which ten minutes.

Worse, generated code reads better than human code at the surface. Consistent naming, complete JSDoc, tidy error handling, no commented-out experiment. That polish suppresses a reviewer's instinct. Sloppy human code signals where to look. Generated code gives no such signal, so reviewers skim.

What to change, given that:

  • Cap the diff, not the time. Review throughput per person did not go up. If PR size doubles, review quality halves. Hold the old size limits and push back on large PRs harder than before.
  • Read the tests first. If they only assert what the implementation happens to do, the PR is unverified no matter how it looks.
  • Make the author the interpreter. A reviewer should be free to ask "what happens here if this call fails" about any line, and the author should answer without going back to the tool. If they cannot, the code is not ready, whether or not it is correct.
  • Review the absences. The common generated defect is something missing: no pagination, no rate limit, no ownership check, no transaction around two writes that must both land.
  • Grep for the tells. Unused parameters, unreachable error branches, defensive checks for states your types make impossible, a helper that duplicates one three directories away, magic values inlined rather than pulled from config.

One structural change is worth naming: the scarce resource on a team is now senior review attention, not senior writing throughput. That is worth thinking through before you extend a team with outside engineers, because review capacity has to grow with the code, not with the headcount.

Generated code needs a different kind of test, not more tests

Human bugs cluster where the author found the problem hard. You can feel where a colleague struggled by reading their code, and that is where you test. Generated bugs cluster somewhere else entirely: where the request was ambiguous. The model does not hesitate, so nothing in the output marks the spot.

Three consequences.

Do not generate tests from the implementation. If the same misunderstanding produces both the code and its tests, you get a green suite that proves only that the two agree. Write the test from the requirement, ideally before the implementation exists, then generate against it. Reversing that order is the most common way teams get a false sense of coverage.

Verify the test, not just the code. Break the implementation on purpose and check the suite goes red. A generated test that mocks the module under test, asserts a call count, or asserts expect(result).toBeDefined() is decoration. Ten seconds per test, and the highest-value ten seconds in the loop.

Shift weight toward integration and property tests. Unit tests inherit the author's assumptions, and generated ones inherit the model's. A test that exercises a real HTTP route against a real database catches the semantic drift that unit tests are structurally blind to. Property-based tests earn their keep for the same reason: generated inputs share nobody's mental model of a reasonable input. Boundaries to cover explicitly: empty collections, duplicate identifiers, a second concurrent write, a date crossing a timezone boundary, a user who owns nothing.

Generated UI lands better than generated backends, with one catch

Interface work is the easiest win available, because a browser is an instant oracle. A form with validation, a filterable table, an empty state, a responsive layout: you see whether it is right in seconds, and the cost of a wrong one is a redo, not a data incident.

The catch is that a model reaches for raw markup. Unless it is told otherwise, it will hand you a <button> with utility classes instead of your Button component, a fourth shade of grey that is not in your palette, and its own spacing scale. Nothing is broken. Your design system quietly stops being the source of truth, and six months of that is a refactor nobody budgeted.

Accessibility drifts the same way and matters more: a div with an onClick and no keyboard handler, an input with a placeholder and no label, a modal that does not trap focus or restore it on close, contrast that fails at the grey the model picked. Cheap to catch with a linter and an axe pass in CI, expensive to retrofit across fifty screens.

The fix is context, not vigilance. Give the model the component inventory and the tokens, ask for composition of existing components, and say explicitly that raw elements are not acceptable. What makes that possible is writing the design guidelines down instead of leaving them in people's heads. On Nestlet, our in-house marketplace for short-term residential leases, a full design guideline document was written up front precisely so the build could be handed to developers or to coding agents and come back consistent either way. That document is the thing you paste in, and it is worth writing before the first screen rather than after the fiftieth.

What it does to junior developers and to how a team learns a codebase

The old way a developer learned a system was by getting stuck in it. You read code because you had to, failed to find the thing, asked someone, and in the asking you built a map of the system and a relationship with the person who owned it. Generation removes the getting stuck. It also removes the map.

The failure mode is specific and you will recognise it: a developer who can ship a feature and cannot debug it. Everything works until it does not, and then there is no model of the system to reason from, only a tool that was not present when the request went out at 2am.

What seems to work:

  • Give juniors bugs, not greenfield features. Debugging is the part of the job generation has barely touched, and it forces the map. A week of real bug tickets teaches more about a codebase than a month of new endpoints.
  • Use the tool for reading, deliberately. "Explain this module and tell me what calls it" is a fast and legitimate way to onboard, and the answer is checkable against the code.
  • Pair on review rather than on writing. Watching a senior read a diff and say what worries them transfers judgement. Watching one type does not, any more.
  • Write the onboarding docs. They have two audiences now. The conventions file that stops a new hire guessing is the file that stops the model guessing.

None of this is an argument against giving juniors these tools. It is an argument that the apprenticeship path has to be rebuilt on purpose, because the accident that used to provide it is gone.

Estimates got less reliable, not more

Two things happened to estimation at once and they point in opposite directions. The mean came down, because the writing step genuinely compressed. The spread widened much more, because a task now either finishes in a tenth of the expected time or turns into an afternoon of steering, and you cannot tell which from the ticket. Teams that quoted the new mean and ignored the new spread have been missing dates.

The practical move is to estimate writing and verification as separate numbers, because only the first one compresses. If a feature was two days of writing and three days of review, integration, edge cases and QA, then halving the writing gets you from five days to four, not to two and a half. Anyone promising the second number is quoting the demo.

Be blunt too about which parts of a project were never typing-bound. Stakeholder decisions, third-party sandbox access, migrating data that is dirtier than anyone admitted, security and compliance review: none of these moved. On most web application builds they are what actually sets the date.

One more effect shows up after launch rather than during the build. Generation lowers the cost of adding code and does nothing about the cost of owning it. Our published planning figure from operating Denti360, our dental practice management product, is fifteen to twenty percent of the original build cost per year in ongoing engineering just to keep it current, before new features. That percentage applies to a codebase. If the codebase is larger because it was cheap to produce, the number it multiplies is larger too.

Context discipline turned into a real engineering skill

The part of this that looks like a fad and is not: choosing what the model sees is a skill, with a visible gap between people who have it and people who do not. It has nothing to do with clever phrasing. It is closer to writing a good ticket.

  • Specify the interface first. Write the type signature, the schema, or the failing test yourself, then ask for the body. Almost all ambiguity failures are failures to pin the contract.
  • Ask for a plan before code. A five-line plan is cheap to read and cheap to correct. Four hundred lines built on a wrong plan are not.
  • Paste the real thing. The actual error, the actual schema, the actual neighbouring file that shows the convention. Summarising the context by hand is where most of the information gets lost.
  • Keep the task small enough to hold in your head. "Build the checkout" produces something you cannot review. "Write the function that turns a cart into line items, given these types" produces something you can.
  • Keep a current conventions file. Error handling, layering rules, naming, date storage, pagination defaults. It pays for itself with human joiners regardless.
  • Treat everything the tool touches as untrusted. No production credentials in context, no agent with write access to production data, a human commit on every change.

The parts of the job it has not touched

Being specific about this matters, because this list is where the remaining value of an experienced engineer sits.

Deciding what to build is untouched. So is saying no to a feature, sequencing work against a launch date, and noticing that the thing the customer asked for is not the thing they need. Production ownership is untouched: the 2am page, the call on whether to roll back or fix forward, an incident where three things are wrong at once. Data migration is untouched, because the difficulty lives in the specific mess of your specific data. Legacy archaeology is untouched wherever the knowledge is in people rather than in the code.

Threat modelling is untouched, and this one is easy to get wrong: a model will happily generate a security control, but deciding which controls you need requires knowing what an attacker wants from your particular business. Naming your domain correctly is untouched. And so is accountability, because the name on the commit is a person's, and whether it should have shipped is theirs to answer.

The honest summary is that generative AI moved a competent developer's bottleneck from producing code to judging it. That is a genuinely good trade for anyone with judgement, a bad one for anyone who was relying on typing speed to look productive, and a reason to invest in review, tests and written conventions rather than in seat count.


Adopting these tools across a team and unsure what it does to your review capacity, your test strategy or your dates? A Scoping Sprint ($2,300, two weeks) ends with a technical plan built for your codebase and team, a clickable prototype, and a fixed quote for the build. Or just start a conversation.

Frequently asked

What are the main generative AI developer trends worth paying attention to?
Four hold up. Code generation has moved the bottleneck from writing to reviewing, so senior review attention is now the scarce resource. Testing practice is shifting toward integration and property tests because unit tests inherit the model's assumptions. Context selection has become a real skill with a visible gap between developers. And estimates have lower means but much wider spread.
How is generative AI actually used in web development day to day?
Mostly for work whose output is cheap to check: interface components against an existing design system, type definitions and validation schemas from a sample payload, one-off scripts and data backfills, tests against an implementation you already trust, and reading unfamiliar code. It is a poor fit for authorisation logic, money handling, concurrency and cache invalidation, where a mistake stays invisible for weeks.
Is generative AI the future of web development, or is it overhyped?
Both, in different places. The gain on verifiable work is real and permanent, and no team that has it will give it back. The claim that it collapses project timelines is overhyped, because typing was never the long pole. Deciding what to build, integrating with an existing system, migrating real data and proving nothing broke did not get faster.
Does generated code need more tests than handwritten code?
It needs different tests rather than more. Human bugs cluster where the author found the work hard; generated bugs cluster where the request was ambiguous, and nothing in the output marks that spot. Write tests from the requirement rather than from the implementation, break the code deliberately to confirm the suite goes red, and weight integration tests over heavily mocked unit tests.
How should code review change when a lot of the diff was generated?
Stop assuming the author reasoned about every line, because typing no longer forces that. Hold your old pull request size limits, read the tests before the implementation, and expect the author to explain any line without returning to the tool. Look hardest for what is absent: a missing ownership check, no pagination, no transaction around two writes that must both land.
What are the most common failure modes of AI generated web code?
Methods that do not exist or belonged to an older library version, code that compiles and encodes a subtly different rule than you asked for, silent over-engineering with error-swallowing try/catch blocks, drift away from your codebase conventions, and confident debugging hypotheses that read exactly like correct ones. The dangerous group is the second, because it passes the happy path.
Is generative AI bad for junior developers?
It removes the accident that used to teach them. Developers learned a system by getting stuck in it, and generation removes the getting stuck along with the mental map. The fix is deliberate: give juniors bugs in existing code rather than greenfield features, use the tool for reading and explaining modules, and pair on code review instead of on writing.
Should we quote shorter timelines now that developers use generative AI?
Only for the writing portion, and quote it separately. If a feature was two days of writing plus three days of review, integration and QA, halving the writing gets you to four days rather than two and a half. Stakeholder decisions, third-party sandbox access, data migration and compliance review did not compress at all, and they usually set the date.
Fixed price · $2,3002-week sprint

Building something in this space?

We turn ideas into buildable plans in 2 weeks: clickable prototype, technical plan, fixed quote. Fixed price, credited against the build.

See the Scoping Sprint

Explore how generative AI tools can revolutionize your web development projects!

Start a project →
Book a 15-min scoping call