When AI Writes 80% of the Code, What Does the Engineer Do?

AI code generation… should it be 100%? A PDF bug can look like a typography problem. Until you add another language. Then it becomes an architecture problem.
In this case, longer translated copy was being clipped because an existing PDF generator relied on fixed-height pages and rasterized the output. The immediate temptation might have been to adjust fonts, page heights, or spacing. Instead, the team replaced the rendering pipeline.
The new implementation moved from html2pdf.js to a declarative, typed model rendered with @react-pdf/renderer, with automatic pagination and vector output. It also introduced a shared core for three report types, an SVG-to-React-PDF code generator, and a runtime feature flag controlling the migration.
And there is another interesting number attached to this feature:
AI-assisted implementation: approximately 80%.
With an important caveat: that is a low-confidence estimate, based on the structure and scale of the implementation rather than direct keystroke or session evidence. It does not mean 80% of the code was generated by AI and accepted unchanged. The available evidence shows substantial human steering and rework.
So what does an engineer do when AI can produce most of the implementation? Quite a lot.
First, you have to know what problem you’re actually solving
The original problem was not “build a new PDF renderer.”
It was localized content being clipped.
That distinction matters.
The engineer identified that fixed-height raster pages were fundamentally poorly suited to variable-length translated content. The replacement therefore used automatic pagination and vector text rather than trying to make the old approach tolerate more languages.
That is the first decision in the entire feature.
And it happens before the AI has much to implement.
AI code generation can propose a new renderer.
It cannot discover from the code alone that the product should stop treating page height as a fixed constraint.
The new stack was a deliberate change in architecture

@react-pdf/renderer became responsible for PDF layout and rendering. TypeScript defined the typed content models. i18next handled language-specific catalogs. SVGO processed SVG artwork before a custom generator converted it into React-PDF components. d3 supplied chart geometry, and Vitest covered the model builders, rendering behavior, and code-generation drift.
A patched dependency sits in the middle of it.
fontkit 2.0.4 had a glyph-cache problem that could affect line breaks when a second document was rendered in the same session. The team patched it in the repository and added a regression test.
That is a very modern frontend stack problem.
The application isn’t just “React.”
It is a chain of specialized libraries, each with its own assumptions about fonts, SVGs, layout, localization, rendering, and browser behavior.
The engineering challenge is making those assumptions agree.
AI code generation can build the vertical slice
The first implementation was substantial.
One commit introduced a complete vertical slice across the model, components, fonts, feature flag, and tests. The feature eventually involved roughly 263 files and around 18,000 added lines, although generated modules and SVG assets inflate those numbers.
AI code generation work was especially strong in code generation.
It produced report model builders, block renderers, the report-agnostic core, chart components, and the SVG code-generation tooling. The implementation added around 40 new PDF component files.
It also generated substantial testing.
Approximately 651 lines were added across six test files, covering model builders, rendering smoke tests, page numbering, inline markup parsing, and the SVG code-generation drift test.
That is where the 80% estimate becomes interesting.
AI code generation was not simply triggering isolated functions.
It was producing coherent chunks of architecture.
But the first implementation was not the final architecture
One of the better decisions came after the first report worked. The team did not design the perfect abstraction for all three reports upfront. They built Report A, then extracted the report-agnostic core, then let Reports B and C reuse it.
That sequencing matters. When AI code generation makes abstraction cheap, it is tempting to generalize everything immediately. But you can also use implementation to discover the abstraction. Build one real case, find what is genuinely shared, and extract it only then.
The result was a common page/section/block model with report-specific pieces sitting on top of a shared rendering core.
Then the weird bugs arrive
The most interesting bugs were not necessarily the ones you would predict from the architecture diagram.
Rendering a second PDF in the same session caused line breaks to shift.
We traced the root cause to a glyph cache issue in fontkit, where cached glyph information didn’t carry the necessary code-point information. The team patched the dependency and added a regression test.
There were also issues around textkit hyphenation, Safari and Firefox blob URL timing, and SVG rendering constraints.
This is where AI code generation development becomes less like “generate code” and more like iterative engineering with a very fast implementation partner.

The machine can participate in every step.
But the loop’s quality depends on someone recognizing which evidence matters.
One of the best decisions was to fix the data, not the translator
At one point, the implementation considered normalizing whitespace in the translation layer.
The engineer redirected that approach.
Instead of teaching the translator to compensate for problematic content, the engineer removed hard line breaks from the translation catalogs themselves.
That fixed the underlying data instead of adding another piece of translation-specific logic.
This is exactly the kind of decision that can disappear when we talk about AI-assisted coding only in terms of productivity.
The AI code generation can produce the normalization function.
The harder question is whether the normalization function should exist.
“Looks good” was enough
There was another deliberate boundary. The new PDFs didn’t need to preserve the same page count as the legacy PDFs. They needed to look good.
That sounds obvious, but pixel-for-pixel parity can become a massive constraint when replacing a rendering engine. The team explicitly accepted different page counts rather than turning the migration into a search for perfect legacy reproduction. That is not a technical limitation. It is a product decision, and it removes an entire category of unnecessary engineering work.
The feature flag was not just deployment plumbing
The new renderer sat behind a runtime flag.
That flag mattered because the backend permanently stores the first uploaded PDF. If a new rendering path produces a bad document, simply switching the UI back doesn’t necessarily undo what’s already been stored.
The team therefore kept a kill switch and chose to fail closed rather than silently falling back to the old renderer when required inputs were missing.
Again, the implementation is straightforward.
The reasoning is not.
The system’s risk profile depends on what happens after the PDF is generated.
That information sits outside the rendering function itself.
The designer is part of the technical system too
The SVG pipeline introduced another constraint.
Artwork had to be exported in a format the PDF renderer could reliably handle, including outlined text and supported SVG structures. Unsupported features such as certain text, filters and masks were configured to fail rather than silently produce incorrect output.
A custom SVG-to-React-PDF generator was added, along with a drift test comparing generated output against the source SVG.
This is another good example of why “tech stack” is bigger than the package.json.
The stack includes the handoff between design tools and code, the format designers export, the assumptions the renderer makes about SVG, and the tests that ensure those assumptions don’t quietly drift.
So what does the engineer actually do?
The feature took about three calendar days.
The available evidence estimates roughly 80% AI code generation implementation, with a 70–90% range and low confidence. The team consisted of two engineers and one designer.
But the human contribution was not simply the remaining 20% of typing.

These decisions shaped what the AI was actually building.
The interesting number isn’t 80%
The 80% figure is useful. AI code generation shouldn’t reach higher levels. It tells us something about how much implementation capacity AI can provide to a small team.
But it doesn’t tell us how much engineering was automated.
A more interesting observation is that AI-assisted implementation and human engineering are not interchangeable. AI can generate a large amount of technically coherent work.
Humans still decide which problems are worth solving, which constraints matter, which risks are acceptable, what belongs in scope, what to generalize, and what to leave deliberately unfinished.
In this case, AI code generation replaced an entire PDF rendering pipeline in about three days. But the engineering work didn’t reduce to writing less code; it shifted toward directing more of it.
And perhaps that is the more important change AI code generation is bringing to software development.
When implementation becomes abundant, judgment becomes part of the architecture.




