JournalFrontendN° 003 / 2026

I Built a Portfolio With AI. Here's What Actually Worked.

AI won't replace software engineers. But it will separate the ones who know how to use it from the ones who don't. Here's what building a real project with AI actually taught me.

Cutting through the noise
Cutting through the noise

There's a lot of noise right now about AI replacing software engineers. I want to offer a different perspective, one that comes from actually building with it, not theorising about it.

AI won't replace software engineers. But it will separate the ones who know how to use it from the ones who don't. That distinction isn't about who writes the best prompt. It's about craft, structure, and intentionality.

When people say AI does "80% of the work," they're measuring the wrong thing. Yes, it generates scaffolding fast. But the remaining 20% is where real engineering happens: integration, architectural decisions, edge cases, making the output actually reflect your project's principles. Skip that 20% and you get code that technically works and gradually becomes unmaintainable.

I needed to feel this in practice, not read about it. So I built a portfolio on the side of my everyday work, fragmented time, weeks-long gaps, inconsistent context. AI handles that surprisingly well. It doesn't care if you haven't touched a project in three weeks. You hand off a chunk of work and come back to something usable.

One project isn't a study. But it was enough to surface some patterns worth examining, about where AI genuinely helps, where it flatters you into thinking it has, and what that might mean for how engineers work with it going forward.

Principle 1: Plan, Plan, Plan

The biggest mistake I see people make with AI is treating it like a vending machine. Put a requirement in, expect working code out. That works for trivial tasks. As soon as real complexity enters, business logic, multiple systems, non-trivial UI, it falls apart fast.

The shift that changed how I work was entering plan mode before touching any code. I used the agent as a thinking partner first: collecting requirements, brainstorming approaches, evaluating trade-offs, mapping architectural decisions. Execution came after.

This is where AI has the most leverage. Discovery and evaluation tasks that used to take real time, researching patterns, weighing options, stress-testing an approach, compress dramatically. The planning phase is where you get the biggest return. Skipping it is where things go wrong.

The time ratio feels counterintuitive. Thirty minutes planning, thirty minutes building. People who are used to just starting resist it. But the output quality difference is consistent enough that it's now the first thing I'd tell someone who's frustrated with AI-generated code.

Principle 2: Match the Reasoning Level to the Task

Not every problem deserves the same depth of thinking. This sounds obvious, but it's something most people don't apply deliberately when working with AI, and the cost of getting it wrong compounds quickly.

A minor UI fix, adjusting padding, tweaking a colour, doesn't need extended reasoning. Minimal context, fast solution. A complex animation bug that touches multiple layers, has timing dependencies, and behaves differently across browsers? That needs something different: a model that thinks through the problem space, surfaces trade-offs, and proposes approaches rather than just solutions.

I hit both in this build. The padding fix took a minute. The scroll-triggered animation bug took longer, and the way it resolved is worth describing in detail.

The animation used an overlapping effect I'd specifically designed for mobile. It was a deliberate UI decision, not an accident. When clipping started occurring, every solution the agent proposed removed the overlap on mobile to fix it. Technically correct. Wrong answer. The model was resolving the symptom without understanding the intent, and with a standard prompt, there was no way for it to know the difference.

What changed the output was changing the reasoning level and reframing the prompt explicitly: preserve this behaviour on mobile, the overlap is intentional, find a solution that fixes the clipping without removing it. With that context and a deeper reasoning pass, the agent stopped treating the feature as the problem and started working around it.

That's the real lesson. The judgment call about what kind of problem you're actually facing, and how to frame it so the model understands your intent, not just the symptom, is still entirely on you.

Principle 3: Use MD Files to Drive Consistency

One of the subtler problems with AI-generated code is consistency. Left to its own devices, an agent will solve the same problem three different ways across three different sessions. In a small project that's annoying. In a larger one it's a real maintenance problem.

The solution I landed on was CLAUDE.md, a markdown file that Claude reads before executing any task. You define your architectural principles, coding conventions, and project-specific patterns once. The agent operates within them.

The value here isn't documentation for its own sake. It's that consistency stops being something you manually enforce or argue for in every session, it's established upfront. Without these files, you're re-explaining your project's logic every time. With them, the agent has a stable model of how the codebase works and makes decisions accordingly.

One caveat: this only works if the file is accurate and maintained. An outdated CLAUDE.md that contradicts your actual codebase creates its own problems, the agent will follow the file, not the code.

Principle 4: Choose an AI-Friendly Tech Stack

Stack choices have always had downstream consequences. With AI in the workflow, that's still true, but the calculus shifts slightly. Some technologies are meaningfully easier for agents to work with, and it's worth being deliberate about that.

For styling I chose Tailwind over plain CSS or SASS. The reason is practical: Tailwind is a constrained, predefined set of utility classes. The agent isn't inventing CSS, it's selecting from known options. That produces more predictable output and fewer hallucinated property values. It also maps cleanly onto how tools like Figma export styles, which matters for the design-to-code step.

For content I used Sanity. It stores content as structured data rather than HTML, which means when an agent queries it, it gets clean, semantically organised information it can reason about reliably. The alternative, unstructured content, scraped HTML, produces noisier, less reliable results.

For the framework I used Next.js, partly because it now supports MCP (Model Context Protocol), which lets AI agents see your running application, understand routing structure, and access live state. In practice this means the agent can diagnose errors and performance issues with actual context rather than guessing from static code.

The broader point isn't that these specific tools are the only valid choices. It's that the ecosystem is converging around AI compatibility, and ignoring that when making stack decisions means leaving real workflow gains on the table. The best AI output comes from giving the agent the least ambiguous possible environment to work in.

Principle 5: Your Figma Setup is a Force Multiplier

This one surprised me more than anything else in the build. The quality of AI output in the design-to-code step isn't primarily determined by the prompt or the model, it's determined by the quality of the Figma file.

The difference is concrete. With a poorly structured file, inconsistent spacing, no design tokens, components that exist visually but not structurally, the generated code technically renders the design but doesn't reflect any underlying system. Spacing gets hardcoded. Components aren't reusable. The code can't adapt when the design changes, because there was no real structure to map to in the first place.

With a well-structured file the translation becomes almost mechanical. That means a few specific things in practice.

Design variables that map directly to your token system, colours, spacing, typography, so the agent references named values rather than hardcoded ones. When a variable changes in Figma, the code reflects it cleanly.

Component variations defined explicitly in Figma rather than implied visually. When the agent can see that a button has default, hover, and disabled states as structured variants, not just as separate frames, it generates code that handles those states properly rather than approximating them.

Annotations for component behaviour, scroll interactions, animation triggers, responsive rules, give the agent intent that isn't visible in the static design. Without them, it guesses. With them, it implements.

When these are in place, Claude maps directly to your component library. Spacing references tokens instead of arbitrary pixel values. The gap between design intent and working code shrinks to the point where the manual translation layer mostly disappears.

That's where productivity gains actually come from. Not from AI writing more code faster, but from eliminating the interpretive work between design and implementation. When the upstream structure is solid, the agent doesn't have to guess.

The deeper principle connects back to everything else in this piece: AI follows the guardrails you set upstream. A well-structured Figma file is a guardrail. So is a good plan, a well-maintained CLAUDE.md, a stack that reduces ambiguity. The engineer's craft hasn't gone away, it's just showing up earlier in the process than it used to.

What This Doesn't Solve

None of these principles make AI a clean solution. Two problems in particular don't go away regardless of how well you set things up.

The first is technical debt. Planning and consistency guardrails reduce it significantly, but across longer projects, subtle drift accumulates between sessions. A slightly different naming pattern here, a component structured just differently enough there. Nothing that breaks the build, but the kind of quiet inconsistency that makes a codebase harder to navigate over time. It's manageable, but it needs watching.

The second is token consumption. On a modular project with real complexity, multiple components, interconnected systems, evolving context, the agent's ability to maintain coherent understanding across sessions degrades. Context windows have limits. Longer projects hit them. You end up managing what the agent knows as an explicit part of your workflow, which adds overhead that's genuinely hard to predict or control.

These aren't reasons to avoid the approach. But they're worth naming honestly, because most writing about AI-assisted development glosses over them. The productivity gains are real. So is the friction that comes alongside them.

Closing

None of this is a formula. One project, one stack, one developer's workflow, the patterns I noticed are worth interrogating, not copying wholesale.

But the through-line feels solid enough to state plainly: AI rewards upstream quality. Better plans, better structure, better inputs, these compound in ways that shortcuts don't. The engineers I've seen struggle with AI tooling aren't struggling because the technology is overhyped. They're struggling because they're bringing it vague problems and expecting specific answers.

The craft hasn't gone away. It's just moved. And in some ways the move is clarifying, it's harder to hide behind busyness when the bottleneck is thinking quality rather than typing speed.

Written by
Federico Corradi
Published
May 22, 2026
Reading time
9 min read
Topics
Frontend, Architecture
Edition
N° 003 / 2026
FrontendArchitecture