<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex Rebula</title>
    <description>The latest articles on DEV Community by Alex Rebula (@alexrebula).</description>
    <link>https://dev.to/alexrebula</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3886918%2F0b21d75b-acbe-4515-bfc6-74e371b3bef6.png</url>
      <title>DEV Community: Alex Rebula</title>
      <link>https://dev.to/alexrebula</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexrebula"/>
    <language>en</language>
    <item>
      <title>I Used AI for 90% of my Portfolio Website — Am I Cheating?</title>
      <dc:creator>Alex Rebula</dc:creator>
      <pubDate>Sun, 19 Apr 2026 06:36:49 +0000</pubDate>
      <link>https://dev.to/alexrebula/i-used-ai-for-90-of-my-portfolio-website-am-i-cheating-10dm</link>
      <guid>https://dev.to/alexrebula/i-used-ai-for-90-of-my-portfolio-website-am-i-cheating-10dm</guid>
      <description>&lt;p&gt;I am currently building my new portfolio site with extremely heavy use of AI (Claude + Copilot). Probably around 90% of the code, documentation, tests, and even some of the architectural thinking have been accelerated or directly generated by AI tools.&lt;/p&gt;

&lt;p&gt;And I’m completely fine with that — the same way I am completely fine pasting my own ideas, sweat and experience into a prompt in bullet points and thoroughly revising the result before publishing the article like in this case. As times change, tools change, and people should move with them and add human value to the ever-improving toolset. &lt;/p&gt;

&lt;p&gt;But the question still comes up (both from others and occasionally from myself): "Am I cheating?"&lt;/p&gt;




&lt;h2&gt;
  
  
  My Honest Answer
&lt;/h2&gt;

&lt;p&gt;No, I’m not cheating.&lt;/p&gt;

&lt;p&gt;I use AI aggressively for speed, but I follow a deliberate, systematic process to stay in full control and maintain ownership of the code.&lt;/p&gt;

&lt;p&gt;Every generated piece goes through structured review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I maintain detailed TODO lists in Markdown files for every major section and component.&lt;/li&gt;
&lt;li&gt;I write tests (Vitest) for critical data factories and components.&lt;/li&gt;
&lt;li&gt;I force myself to understand and be able to explain every function and major decision.&lt;/li&gt;
&lt;li&gt;I regularly push back on AI suggestions that feel off, overly clever, or not aligned with existing codebase patterns.&lt;/li&gt;
&lt;li&gt;I treat AI output as a strong first draft — never the final product.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This review discipline is something I’m actually proud of. It’s how I make sure the massive speed AI gives me doesn’t come at the cost of depth or long-term maintainability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where AI Genuinely Helps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It removes boring boilerplate and lets me move extremely fast.&lt;/li&gt;
&lt;li&gt;It surfaces patterns and solutions I might not have considered immediately.&lt;/li&gt;
&lt;li&gt;It helps me maintain consistency across a large number of components.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A Concrete Example from This Project
&lt;/h2&gt;

&lt;p&gt;When I asked the AI to silence Iconify’s “icon loaded online” warnings, it suggested importing large JSON packages (&lt;code&gt;@iconify-json/solar&lt;/code&gt;, &lt;code&gt;@iconify-json/logos&lt;/code&gt;, &lt;code&gt;@iconify-json/simple-icons&lt;/code&gt;) and writing a &lt;code&gt;pickIcons()&lt;/code&gt; helper. It compiled and the warnings disappeared — technically “correct.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before — what AI suggested:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// icon-sets.extra.ts (AI version)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;icons&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;logosIcons&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@iconify-json/logos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;icons&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;solarIcons&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@iconify-json/solar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pickIcons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IconifyJSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="nx"&gt;IconifyJSON&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]]))&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;addCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pickIcons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logosIcons&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;angular-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]));&lt;/span&gt;
&lt;span class="nf"&gt;addCollection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pickIcons&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;solarIcons&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code-bold&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;settings-bold&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]));&lt;/span&gt;
&lt;span class="c1"&gt;// Ships the entire @iconify-json/logos (7.3 MB) and @iconify-json/solar (6.3 MB) packages.&lt;/span&gt;
&lt;span class="c1"&gt;// JSON doesn't tree-shake. You import it, you ship it — all 9,495 icons of it.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it was the wrong solution.&lt;/p&gt;

&lt;p&gt;The codebase already had a clean, established pattern in &lt;code&gt;icon-sets.minimals.ts&lt;/code&gt;: inline SVG strings, no extra package imports, explicit &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; control. The AI didn’t see or follow that pattern — it reached for the general Iconify documentation approach instead of reading the existing code first.&lt;/p&gt;

&lt;p&gt;I caught it, pushed back, and we rewrote it properly: no heavy imports, only the exact icons we use, explicit dimensions, and clear comments explaining the rules for anyone who adds icons later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After — the correct pattern already in the codebase:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// icon-sets.extra.ts (correct version)&lt;/span&gt;
&lt;span class="c1"&gt;// Rule: inline SVG body only. For logos: collection, always include explicit width + height&lt;/span&gt;
&lt;span class="c1"&gt;// because register-icons.ts forces the collection default to 24×24, which clips non-square paths.&lt;/span&gt;
&lt;span class="nf"&gt;addCollection&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;logos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;angular-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;271&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;path fill="#E23237" d="M0 134.5L11.5 ..." /&amp;gt;&amp;lt;path fill="#B52E31" d="..." /&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// React logo is square — no override needed, inherits collection 24×24&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;g fill="#61DAFB"&amp;gt;&amp;lt;circle cx="12" cy="12" r="2.05" /&amp;gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ... one entry per icon, exactly the ones we use&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the real difference:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Raw size&lt;/th&gt;
&lt;th&gt;Icons available&lt;/th&gt;
&lt;th&gt;Icons we actually use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@iconify-json/solar&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6.3 MB&lt;/td&gt;
&lt;td&gt;7,404&lt;/td&gt;
&lt;td&gt;105&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@iconify-json/logos&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;7.3 MB&lt;/td&gt;
&lt;td&gt;2,091&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@iconify-json/simple-icons&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4.6 MB&lt;/td&gt;
&lt;td&gt;3,693&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;18.2 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;13,188&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;118&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The correct approach — 118 inline SVG strings across two registration files — weighs around &lt;strong&gt;75 KB total&lt;/strong&gt;. That is a &lt;strong&gt;247× difference&lt;/strong&gt; in payload.&lt;/p&gt;

&lt;p&gt;There was also a correctness bug hiding in the AI’s lazy approach: &lt;code&gt;logos&lt;/code&gt; icons have a 256×256 viewBox, but &lt;code&gt;register-icons.ts&lt;/code&gt; forces all non-carbon collections to default to 24×24. The &lt;code&gt;pickIcons()&lt;/code&gt; approach copied only &lt;code&gt;body&lt;/code&gt; and dropped the metadata, so icons like &lt;code&gt;logos:typescript-icon&lt;/code&gt; would have silently rendered with the wrong dimensions at any non-standard render size.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Audit That Proved the Point
&lt;/h2&gt;

&lt;p&gt;Once the inline SVG pattern was correct, I asked the AI to audit the codebase and confirm every icon was registered. It scanned every &lt;code&gt;.ts&lt;/code&gt; and &lt;code&gt;.tsx&lt;/code&gt; file for &lt;code&gt;icon="prefix:name"&lt;/code&gt; JSX attributes and declared zero missing icons.&lt;/p&gt;

&lt;p&gt;I questioned the numbers anyway. A second scan revealed the real usage: 105 solar icons (not 4). It had also missed 5 &lt;code&gt;logos:&lt;/code&gt; icons that were stored as string values in TypeScript data objects rather than JSX props.&lt;/p&gt;

&lt;p&gt;The test was rewritten to handle both patterns (JSX attributes + string literals) with a whitelist of known Iconify prefixes. The 5 missing icons were added to &lt;code&gt;icon-sets.extra.ts&lt;/code&gt;. All tests now pass against the correct 118-icon count.&lt;/p&gt;

&lt;p&gt;This is exactly the kind of thing a senior developer catches that AI alone misses.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Rules for This Project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;AI is allowed to draft, but I must review, understand, and be able to defend every important piece.&lt;/li&gt;
&lt;li&gt;Critical parts (like the sections-api data layer) get extra scrutiny and manual refinement.&lt;/li&gt;
&lt;li&gt;I will eventually go through the entire codebase and make sure I can explain every function and design decision without referring back to the AI conversation.&lt;/li&gt;
&lt;li&gt;When AI produces a wrong pattern and I correct it, I write a regression test so the mistake can never happen again.&lt;/li&gt;
&lt;li&gt;This portfolio is my test project: use AI aggressively to ship fast, but then do the deeper work to truly own the result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m not ashamed of using AI heavily. I’m actually quite proud of the system I built around it to make sure I stay a better engineer, not a worse one.&lt;/p&gt;

&lt;p&gt;The goal isn’t to write every line myself.&lt;br&gt;&lt;br&gt;
The goal is to ship high-quality work while keeping my skills and understanding sharp.&lt;/p&gt;

&lt;p&gt;That’s the balance I’m aiming for.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
