<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Nemanja Petrovic, Author at ShiftMag</title>
	<atom:link href="https://shiftmag.dev/author/nemanja-petrovic/feed/" rel="self" type="application/rss+xml" />
	<link>https://shiftmag.dev/author/nemanja-petrovic/</link>
	<description>Insightful engineering content &#38; community</description>
	<lastBuildDate>Wed, 29 Apr 2026 11:47:00 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.3</generator>

<image>
	<url>https://shiftmag.dev/wp-content/uploads/2024/08/cropped-ShiftMag-favicon-32x32.png</url>
	<title>Nemanja Petrovic, Author at ShiftMag</title>
	<link>https://shiftmag.dev/author/nemanja-petrovic/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>How I Built a Full-Stack App in 6 Days with the Help of AI</title>
		<link>https://shiftmag.dev/how-i-built-a-full-stack-app-in-6-days-with-the-help-of-ai-7564/</link>
		
		<dc:creator><![CDATA[Nemanja Petrovic]]></dc:creator>
		<pubDate>Fri, 16 Jan 2026 11:47:46 +0000</pubDate>
				<category><![CDATA[Artificial Intelligence]]></category>
		<category><![CDATA[Developer Experience]]></category>
		<category><![CDATA[AI]]></category>
		<category><![CDATA[artificial intelligence]]></category>
		<category><![CDATA[backend]]></category>
		<category><![CDATA[frontend]]></category>
		<category><![CDATA[full stack]]></category>
		<guid isPermaLink="false">https://shiftmag.dev/?p=7564</guid>

					<description><![CDATA[<p>Building an app with AI feels easy - until real users arrive. </p>
<p>The post <a href="https://shiftmag.dev/how-i-built-a-full-stack-app-in-6-days-with-the-help-of-ai-7564/">How I Built a Full-Stack App in 6 Days with the Help of AI</a> appeared first on <a href="https://shiftmag.dev">ShiftMag</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I shipped an MVP in 6 days using Claude Code, but &#8220;clean&#8221; AI code hid major performance pitfalls. From N+1 queries to broken responsive design, this is why a decade of engineering experience remains the ultimate superpower when building with AI.</p>



<p class="wp-block-paragraph">By the end of 2025, I had read a lot about people building entire apps with AI, going from an idea to a product in just weeks or even days. That made me ask myself: how are they doing this?</p>



<p class="wp-block-paragraph">I have <strong>more than ten years of experience in enterprise backend development</strong>. Seeing these fast, AI-built apps sparked my curiosity. I use AI in my day-to-day work, but only for small tasks, nothing close to building a full product.</p>



<p class="wp-block-paragraph">So I thought, why not try it myself and see if it’s really possible?</p>



<p class="wp-block-paragraph">I started thinking about what I actually wanted to build, and it quickly became clear that it had to be something I’d use myself. I’m a runner and I work in enterprise, so the idea came pretty naturally: a running app, but with a twist &#8211; <strong>tracking fitness while mapping it onto a corporate-style career journey</strong>.</p>



<p class="wp-block-paragraph">That’s how RunCorp came to life.</p>



<h2 class="wp-block-heading"><span id="ai-as-a-fast-mvp-builder-and-ui-scaffold">AI as a fast MVP builder and UI scaffold</span></h2>



<p class="wp-block-paragraph">My main stack is Java and Python, but I deliberately avoided what I know best. I wanted something familiar, yet still a bit uncomfortable. For the backend, I chose<strong> Laravel</strong>, since I’ve done some freelance work with it. For mobile, I picked <strong>Flutter</strong>, even though I had never built a mobile app before.</p>



<p class="wp-block-paragraph">I bought a <strong>Claude Code subscription</strong> and started building. I quickly learned that you can’t just tell it to do something and expect a good result. That realization led me to throw away the very first thing it generated.</p>



<p class="wp-block-paragraph">What really works is<strong> using plan mode</strong>. You let the AI analyze the problem, ask you questions, and create a plan. Only after you feel happy with the plan do you let it generate code. It feels more like collaborating with a teammate than typing commands at a tool.</p>



<p class="wp-block-paragraph">The downside is that this approach<strong> burns through tokens</strong>. If you don’t want to pay for extra usage, you have to wait a few hours for the limit to reset. Even so, I managed to build a basic MVP in six days.</p>



<p class="wp-block-paragraph">I started with just the <strong>UI and mocked data</strong>. By the end of the first day, I had an app with all the MVP screens in place. You could actually &#8220;use&#8221; the app &#8211; move from screen to screen and see something -even though real functionality didn’t exist yet. Everything ran on mocked data. For this kind of work, AI proved very effective at generating basic Flutter layouts like this:</p>



<pre class="wp-block-code"><code><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">class HomeScreen</mark><mark style="background-color:rgba(0, 0, 0, 0);color:#1515ed" class="has-inline-color"> </mark>extends <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">StatelessWidget</mark></strong> { 
@override
<strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">Widget</mark></strong> build(<strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">BuildContext context</mark></strong>) {
return <mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color"><strong>Scaffold</strong></mark>(
 appBar: <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">AppBar</mark></strong>(title: <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">Text</mark></strong>(<strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">'RunCorp</mark></strong>')),
  body: <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">ListView</mark></strong>(
   padding: <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">EdgeInsets</mark></strong>.all(16), 
    children: &#91;
   <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">  Text</mark></strong>(
      '<strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">Welcome</mark></strong> back',
     style: <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">Theme.</mark></strong>of(context).textTheme.headlineSmall,
    ),
   <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color"> SizedBox</mark></strong>(height: 16),
    <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">Card</mark></strong>(
     child: <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">ListTile</mark></strong>(
      title: <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">Text</mark></strong>('<strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">Current Level</mark></strong>'), 
      subtitle: <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">Text</mark></strong>(<strong>'<mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">Senior Runner</mark>'</strong>),
      ),
     ),
    ],
   ),
  );
 }
}
</code></pre>



<p class="wp-block-paragraph">Nothing complex, but very fast.<strong> This is where AI clearly shines</strong>: it scaffolds the UI, wires screens together, and handles repetitive setup.</p>



<h2 class="wp-block-heading"><span id="outdated-libraries-and-testing-challenges">Outdated libraries and testing challenges</span></h2>



<p class="wp-block-paragraph">After that, I worked screen by screen, <strong>building front-end and back-end functionality together</strong>. I followed the same workflow every time: create a plan, answer AI questions, refine the plan, answer again, and generate code only after I felt satisfied with the plan.</p>



<p class="wp-block-paragraph">Here’s the first big problem for someone without much experience: when you use plan mode, <strong>the AI asks questions you might not know how to answer</strong>. It asks things like: do you want to use OAuth? How do you want to encrypt passwords? Do you want a separate table for user profiles, or do you want everything in one table? You have to make these decisions early. Otherwise, the AI generates something that either doesn’t work or becomes hard to change later.</p>



<p class="wp-block-paragraph">One concrete backend issue came up during the <strong>Strava integration</strong>. When I asked the AI for help with Strava authentication, it suggested an existing Laravel package.</p>



<p class="wp-block-paragraph">At first glance, the suggestion looked reasonable. But after I checked it more closely, I realized the <strong>package hadn’t received updates for several years</strong> and didn’t support the current Laravel version. It relied on outdated dependencies and failed in a modern Laravel setup.</p>



<p class="wp-block-paragraph">This example shows where AI falls short. It suggests known libraries, but it doesn’t judge whether teams still maintain them or whether they fit today’s ecosystem.</p>



<p class="wp-block-paragraph">Instead of forcing the package into the project,<strong> I implemented the Strava OAuth flow directly using Laravel’s HTTP client</strong>. The core token exchange looked like this:</p>



<pre class="wp-block-code"><code>$response = Http::asForm()-&gt;post(<mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color"><strong>'https://www.strava.com/oauth/token'</strong></mark>, &#91;.                                <mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">  </mark>
    <mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color"><strong>'client_id'</strong></mark>	=&gt; config(<mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color"><strong>'services.strava.client_id'</strong></mark>), 
    <mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color"><strong>'client_secret'</strong></mark> =&gt; config(<mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color"><strong>'services.strava.client_secret'</strong></mark>),
    <mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color"><strong>'code'</strong></mark>	=&gt; $request-&gt;get(<mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color"><strong>'code'</strong></mark>),
    <mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color"><strong>'grant_type'</strong></mark>	=&gt; <mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color"><strong>'authorization_code'</strong></mark>,
]);

$data = $response-&gt;json();
</code></pre>



<p class="wp-block-paragraph"><strong>A similar issue appeared on the frontend</strong>. The AI generated Flutter UI that looked great on a large simulator. Once I tested it on smaller phones, problems started to show up. Text became barely readable, layouts overflowed, and some screens turned difficult to use. A typical example looked like this:</p>



<pre class="wp-block-code"><code><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color"><strong>Text</strong></mark>(
<strong><mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">'Weekly Distance'</mark></strong>,
<mark style="background-color:rgba(0, 0, 0, 0);color:#002afe" class="has-inline-color"><strong>style</strong></mark><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-black-color"><strong>:</strong> </mark>TextStyle(<strong><mark style="background-color:rgba(0, 0, 0, 0);color:#002afe" class="has-inline-color">fontSize</mark></strong>: <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#002afe" class="has-inline-color">24</mark></strong>),
);
</code></pre>



<p class="wp-block-paragraph"><strong>Hardcoded font sizes worked fine on larger screens but failed badly on smaller ones</strong>. Fixing this required a shift toward responsive design. I improved the layout by switching to theme-based typography:</p>



<pre class="wp-block-code"><code>Text(
'Weekly Distance',
style: Theme.of(<mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color"><strong>context</strong></mark>).textTheme.titleMedium,
);
 
LayoutBuilder(
<strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color"> builder</mark></strong>: (context, constraints) { 
  return Text(
   'Weekly Distance', 
  style: TextStyle(
   <strong><mark style="background-color:rgba(0, 0, 0, 0);color:#0e087a" class="has-inline-color">fontSize</mark></strong>: constraints.maxWidth &lt; 360 ? 16 : 20,
   ),
  );
 },
);</code></pre>



<p class="wp-block-paragraph">The UI technically worked before &#8211; it just didn’t work well everywhere. <strong>Issues like this only show up when you test on real devices</strong>.</p>



<h2 class="wp-block-heading"><span id="when-ai-generated-code-meets-real-users">When AI-generated code meets real users</span></h2>



<p class="wp-block-paragraph">After six days, I had an app with enough functionality to be tested by someone. I asked people from my running club if they wanted to try it, and they said yes. I sent them a beta build, and 25 of them started using it. <strong>That’s when the real problems began to appear</strong>.</p>



<p class="wp-block-paragraph">After just two days, people reported that the home screen took a long time to load and that the stats screen kept crashing. That was the moment I realized this was the end of just talking with AI. I had to do what I’ve been doing for years: <strong>actually debug and review all the code</strong>.</p>



<p class="wp-block-paragraph">The AI-generated backend code looked clean. It passed basic tests and worked perfectly with small datasets, so I shipped it. <strong>But once real users arrived, reality hit hard</strong>. Requests were timing out, the stats and home pages barely loaded on mobile, database CPU spiked, and every request ran over 20 queries. Classic red flags.</p>



<p class="wp-block-paragraph">These were problems that wouldn’t have made it to production if I had written the code myself from the start. <strong>Experience teaches you where bottlenecks usually hide</strong>. AI didn’t optimize anything; it just made things work.</p>



<p class="wp-block-paragraph">Here’s what the <strong>AI-generated backend code got wrong</strong> all at once:</p>



<ul class="wp-block-list">
<li>No eager loading, causing N+1 queries</li>



<li>Queries running inside loops</li>



<li>Loading entire models into memory just to calculate aggregates</li>



<li>No caching at all</li>



<li>Missing database indexes</li>



<li>Recalculating values that were already stored</li>



<li>Doing heavy computation in PHP instead of letting the database handle it</li>
</ul>



<p class="wp-block-paragraph"><strong>None of these were bugs &#8211; they were experience problems</strong>.</p>



<p class="wp-block-paragraph">At that point, I had to step in and do what I’ve been doing for years: rewrite the entire statistics pipeline. Some of the key fixes were:</p>



<h3 class="wp-block-heading"><span id="eager-loading-instead-of-n1">Eager Loading Instead of N+1</span></h3>



<pre class="wp-block-code"><code><mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">$user = $request-&gt;user()-&gt;load('profile');</mark></code></pre>



<h3 class="wp-block-heading"><span id="aggressive-response-caching">Aggressive Response Caching</span></h3>



<pre class="wp-block-code"><code><mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">return Cache::remember("user_stats:{$user-&gt;id}", now()-&gt;endOfDay(), </mark>
<mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">function () {
    // heavy calculations

});</mark></code></pre>



<h3 class="wp-block-heading"><span id="query-consolidation-7-queries-%e2%86%92-1">Query Consolidation (7 Queries → 1)</span></h3>



<pre class="wp-block-code"><code><mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">$stats = DB::table('activities')

    -&gt;where('user_id', $userId)

    -&gt;selectRaw("

         SUM(distance_meters) as total_distance, </mark>
         <mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">COUNT(DISTINCT DATE(start_at)) as active_days,
 
         SUM(CASE WHEN start_at &gt;= ? THEN distance_meters ELSE 0 END) as weekly_distance
", &#91;$weekStart])

-&gt;first();</mark></code></pre>



<h3 class="wp-block-heading"><span id="group-by-instead-of-query-loops">GROUP BY Instead of Query Loops</span></h3>



<pre class="wp-block-code"><code><mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">-&gt;groupBy('week_num')</mark></code></pre>



<h3 class="wp-block-heading"><span id="using-stored-values-instead-of-recomputing">Using Stored Values Instead of Recomputing</span></h3>



<pre class="wp-block-code"><code><mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">$longestStreak = $user-&gt;profile-&gt;highest_streak ?? 0;</mark></code></pre>



<h3 class="wp-block-heading"><span id="strategic-indexes">Strategic Indexes</span></h3>



<pre class="wp-block-code"><code><mark style="background-color:rgba(0, 0, 0, 0);color:#00a266" class="has-inline-color">$table-&gt;index(&#91;'user_id', 'start_at', 'distance_meters']);</mark></code></pre>



<h2 class="wp-block-heading"><span id="the-results-were-immediate">The results were immediate</span></h2>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td><strong>Metric</strong><strong></strong></td><td><strong>Before</strong><strong></strong></td><td><strong>After</strong><strong></strong></td></tr><tr><td>Queries per request</td><td>20+</td><td>7–9</td></tr><tr><td>Response time</td><td>&gt;30s (timeout)</td><td>300–500ms</td></tr><tr><td>Cached response</td><td>N/A</td><td>10–50ms</td></tr><tr><td>Memory usage</td><td>200MB+</td><td>40–60MB</td></tr><tr><td>Timeouts</td><td>Constant</td><td>Zero</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">After these changes,<strong> the app finally felt usable in real conditions</strong>. The home screen loaded instantly, the stats screen didn’t crash, and the database handled multiple users at the same time. Experience made the difference.</p>



<p class="wp-block-paragraph">I shipped a working app quickly, but I still had to do a lot of work myself. Coding the basics &#8211; the tasks any junior developer can handle &#8211; is easy, and that’s exactly where AI excels. It can speed up development, but you need to know when to step in, what to change, what to ask, and how to plan everything properly.</p>



<p class="wp-block-paragraph"><strong>Coding isn’t even the hardest part, at least for mobile apps</strong>. You can build the app fast, but you must deploy the backend, set up servers, configure domains, and prepare everything for the app stores.</p>



<p class="wp-block-paragraph">Then comes the part no one talks about: Google Play beta testing, Apple App Store review, waiting for developer accounts and licenses to get approved, fixing small issues reviewers reject, and updating screenshots, descriptions, and privacy policies. None of this is hard, but<strong> all of it takes time</strong>.</p>



<p class="wp-block-paragraph">From the moment I started building to the moment the app went public, <strong>the process took just over a month, still very fast.</strong></p>



<p class="wp-block-paragraph">I realized that today <strong>AI becomes incredibly powerful in the hands of someone who knows what they’re doing</strong>. For experienced engineers, it can provide a huge speed boost, almost like a superpower. AI acts like a team of juniors handling repetitive work while you focus on high-level decisions.</p>



<p class="wp-block-paragraph">Someone with solid experience can now build projects that used to require a small team. Can a non-technical person build an app with AI? Yes, to some extent. They can create something that looks decent and runs, but most of the time it will start falling apart quickly.</p>



<p class="wp-block-paragraph">The main takeaway: <strong>the basics still matter</strong>. Understanding how things work under the hood makes all the difference. AI doesn’t replace experience, but it amplifies it, and that’s where it delivers real value.</p>


<figure class="wp-block-post-featured-image"><img fetchpriority="high" decoding="async" width="1200" height="630" src="https://shiftmag.dev/wp-content/uploads/2026/01/nemanja.png?x32039" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="" style="object-fit:cover;" srcset="https://shiftmag.dev/wp-content/uploads/2026/01/nemanja.png 1200w, https://shiftmag.dev/wp-content/uploads/2026/01/nemanja-300x158.png 300w, https://shiftmag.dev/wp-content/uploads/2026/01/nemanja-1024x538.png 1024w, https://shiftmag.dev/wp-content/uploads/2026/01/nemanja-768x403.png 768w" sizes="(max-width: 1200px) 100vw, 1200px" /></figure><p>The post <a href="https://shiftmag.dev/how-i-built-a-full-stack-app-in-6-days-with-the-help-of-ai-7564/">How I Built a Full-Stack App in 6 Days with the Help of AI</a> appeared first on <a href="https://shiftmag.dev">ShiftMag</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: shiftmag.dev @ 2026-09-02 06:12:12 by W3 Total Cache
-->