<?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: buildbasekit</title>
    <description>The latest articles on DEV Community by buildbasekit (@buildbasekit).</description>
    <link>https://dev.to/buildbasekit</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%2F3822341%2Fd883b38f-434e-4524-aa08-2a8372503386.webp</url>
      <title>DEV Community: buildbasekit</title>
      <link>https://dev.to/buildbasekit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/buildbasekit"/>
    <language>en</language>
    <item>
      <title>Why Most Side Projects Never Launch</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Fri, 22 May 2026 16:07:07 +0000</pubDate>
      <link>https://dev.to/buildbasekit/why-most-side-projects-never-launch-3cgh</link>
      <guid>https://dev.to/buildbasekit/why-most-side-projects-never-launch-3cgh</guid>
      <description>&lt;p&gt;Most side projects don’t fail because the idea is bad.&lt;/p&gt;

&lt;p&gt;They fail during setup.&lt;/p&gt;

&lt;p&gt;You start excited:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New idea&lt;/li&gt;
&lt;li&gt;New repo&lt;/li&gt;
&lt;li&gt;Big plans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then suddenly you're spending days on things that have nothing to do with your actual product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication setup&lt;/li&gt;
&lt;li&gt;JWT configuration&lt;/li&gt;
&lt;li&gt;Roles and permissions&lt;/li&gt;
&lt;li&gt;File uploads&lt;/li&gt;
&lt;li&gt;Email setup&lt;/li&gt;
&lt;li&gt;Security configuration&lt;/li&gt;
&lt;li&gt;Deployment setup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And before you even build the core feature, your momentum is gone.&lt;/p&gt;

&lt;p&gt;That’s where a lot of side projects die.&lt;/p&gt;

&lt;p&gt;Not because the idea was weak.&lt;/p&gt;

&lt;p&gt;Because the setup work drained all the energy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Problem: Boilerplate Fatigue
&lt;/h2&gt;

&lt;p&gt;If you've built backend projects before, you already know the pattern.&lt;/p&gt;

&lt;p&gt;Every new project starts with the same repetitive work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure Spring Security&lt;/li&gt;
&lt;li&gt;Set up JWT authentication&lt;/li&gt;
&lt;li&gt;Create user roles&lt;/li&gt;
&lt;li&gt;Add file upload handling&lt;/li&gt;
&lt;li&gt;Configure email flows&lt;/li&gt;
&lt;li&gt;Prepare deployment-ready structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is your actual product.&lt;/p&gt;

&lt;p&gt;But it eats days.&lt;/p&gt;

&lt;p&gt;Sometimes weeks.&lt;/p&gt;

&lt;p&gt;And for side projects, that delay is often enough to kill momentum completely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Starter Kits Exist
&lt;/h2&gt;

&lt;p&gt;Starter kits exist for one reason:&lt;/p&gt;

&lt;p&gt;To remove repetitive setup so you can focus on building actual features.&lt;/p&gt;

&lt;p&gt;Instead of rebuilding the same backend foundation every time, you start with production-ready infrastructure and move faster.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less setup&lt;/li&gt;
&lt;li&gt;Faster shipping&lt;/li&gt;
&lt;li&gt;More momentum&lt;/li&gt;
&lt;li&gt;Higher chance your side project actually launches&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  If You're Building with Spring Boot
&lt;/h2&gt;

&lt;p&gt;If you're using Spring Boot, I built a free starter kit called &lt;strong&gt;AuthKit Lite&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT authentication&lt;/li&gt;
&lt;li&gt;Role-based access control&lt;/li&gt;
&lt;li&gt;Spring Security setup&lt;/li&gt;
&lt;li&gt;Production-ready backend structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/authkit-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/authkit-lite/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;The hardest part of side projects usually isn’t the idea.&lt;/p&gt;

&lt;p&gt;It’s surviving the setup phase.&lt;/p&gt;

&lt;p&gt;Skip repetitive work.&lt;/p&gt;

&lt;p&gt;Build the actual product.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>productivity</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Spring Boot File Upload Performance Test: 7,500 RPM Benchmark</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Tue, 19 May 2026 18:51:28 +0000</pubDate>
      <link>https://dev.to/buildbasekit/spring-boot-file-upload-performance-test-7500-rpm-benchmark-589h</link>
      <guid>https://dev.to/buildbasekit/spring-boot-file-upload-performance-test-7500-rpm-benchmark-589h</guid>
      <description>&lt;p&gt;This Spring Boot file upload performance test started with a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How far can a Spring Boot file service go under realistic mixed file traffic before latency becomes the problem?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Backend APIs often feel fast in development because we test them with tiny files, a handful of requests, and very polite traffic.&lt;/p&gt;

&lt;p&gt;Production is less polite.&lt;/p&gt;

&lt;p&gt;File upload systems deal with large payloads, concurrent downloads, metadata lookups, deletes, previews, and sometimes video delivery. That combination can expose bottlenecks that never show up in a normal local test.&lt;/p&gt;

&lt;p&gt;So I ran a load test against a Spring Boot file upload service and pushed it gradually up to &lt;strong&gt;7,500 requests per minute&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The short version:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The service stayed alive with zero HTTP failures, but latency started degrading hard after roughly 6,300 RPM. The main bottleneck was not Spring Boot itself. It was serving large MP4 files directly through the backend.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Spring Boot File Upload Test Setup
&lt;/h2&gt;

&lt;p&gt;The benchmark used &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;FiloraFS-Lite&lt;/a&gt;, a lightweight Spring Boot file service boilerplate.&lt;/p&gt;

&lt;p&gt;The service includes the typical file system APIs you would expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file upload&lt;/li&gt;
&lt;li&gt;file download&lt;/li&gt;
&lt;li&gt;metadata lookup&lt;/li&gt;
&lt;li&gt;file listing&lt;/li&gt;
&lt;li&gt;delete operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal was not to prove that this exact setup is universally fast. Benchmarks are always infrastructure-dependent.&lt;/p&gt;

&lt;p&gt;The goal was more practical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;find where latency starts to degrade&lt;/li&gt;
&lt;li&gt;compare small file and large file behavior&lt;/li&gt;
&lt;li&gt;track throughput under increasing load&lt;/li&gt;
&lt;li&gt;observe CPU and memory pressure&lt;/li&gt;
&lt;li&gt;understand whether the app fails suddenly or degrades gradually&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The load runner was written in TypeScript and ramped traffic from &lt;strong&gt;500 RPM&lt;/strong&gt; to &lt;strong&gt;7,500 RPM&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Boot Load Test Summary
&lt;/h2&gt;

&lt;p&gt;Here are the headline numbers from the full test:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total requests&lt;/td&gt;
&lt;td&gt;134,517&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak target load&lt;/td&gt;
&lt;td&gt;7,500 RPM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak P95 latency&lt;/td&gt;
&lt;td&gt;1,878 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP failures&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Average CPU usage&lt;/td&gt;
&lt;td&gt;35.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Main bottleneck&lt;/td&gt;
&lt;td&gt;MP4/video downloads&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94ckedl0x7wywbosdsdh.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94ckedl0x7wywbosdsdh.webp" alt="Spring Boot file upload latency under 7500 RPM load test" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;P95 latency stayed manageable at lower traffic levels, then rose quickly once the test moved into the upper RPM range.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The most encouraging result was stability.&lt;/p&gt;

&lt;p&gt;The service completed more than 134,000 requests without HTTP-level failures. That matters because many systems collapse before you get useful latency data.&lt;/p&gt;

&lt;p&gt;This one did not collapse. It slowed down.&lt;/p&gt;

&lt;p&gt;That distinction is important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Boot Latency Results
&lt;/h2&gt;

&lt;p&gt;At lower load levels, the backend behaved comfortably.&lt;/p&gt;

&lt;p&gt;From &lt;strong&gt;500 RPM&lt;/strong&gt; to around &lt;strong&gt;5,100 RPM&lt;/strong&gt;, P95 latency mostly stayed below &lt;strong&gt;500 ms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After roughly &lt;strong&gt;5,700 RPM&lt;/strong&gt;, latency began climbing faster.&lt;/p&gt;

&lt;p&gt;Once the test crossed around &lt;strong&gt;6,300 RPM&lt;/strong&gt;, P95 latency moved beyond &lt;strong&gt;1 second&lt;/strong&gt;, which is where the user experience would start feeling visibly slow for many workflows.&lt;/p&gt;

&lt;p&gt;In this specific benchmark, I would treat &lt;strong&gt;5,000 to 5,500 RPM&lt;/strong&gt; as the practical operating range if the target is to keep P95 latency around &lt;strong&gt;500 to 600 ms&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Could the system accept more traffic? Yes.&lt;/p&gt;

&lt;p&gt;Would I call the higher range comfortable for production without changing the file delivery architecture? No.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Boot Throughput Results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35yibnuae6i0ioiob1a0.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35yibnuae6i0ioiob1a0.webp" alt="Spring Boot file upload throughput under increasing load" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Throughput tracked the target RPM well until higher load levels, where the service started falling behind.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The backend handled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;more than 134,000 total requests&lt;/li&gt;
&lt;li&gt;200 concurrent workers&lt;/li&gt;
&lt;li&gt;target traffic up to 7,500 RPM&lt;/li&gt;
&lt;li&gt;mixed file workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Throughput followed the target load reasonably well until the upper stages of the ramp test, where the system started falling behind.&lt;/p&gt;

&lt;p&gt;That is a useful signal. It means the application was not failing because controllers, routing, or metadata APIs were broken. It was reaching a physical limit around file delivery.&lt;/p&gt;

&lt;p&gt;For a file service, that is exactly the kind of thing you want to discover before real users discover it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  File-Type Latency: MP4 Was the Real Bottleneck
&lt;/h2&gt;

&lt;p&gt;The most interesting result was the file-type latency breakdown.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0pb55xyk87qv0m1tn7rd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0pb55xyk87qv0m1tn7rd.webp" alt="Spring Boot file upload latency comparison for PDF PNG and MP4 files" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;MP4 downloads had much higher P95 latency than PDF and PNG files.&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File type&lt;/th&gt;
&lt;th&gt;P95 latency&lt;/th&gt;
&lt;th&gt;Observation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PDF&lt;/td&gt;
&lt;td&gt;4 ms&lt;/td&gt;
&lt;td&gt;Very fast&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PNG&lt;/td&gt;
&lt;td&gt;338 ms&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MP4&lt;/td&gt;
&lt;td&gt;1,707 ms&lt;/td&gt;
&lt;td&gt;Main bottleneck&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That result changes how you should interpret the benchmark.&lt;/p&gt;

&lt;p&gt;If the app were slow across every endpoint and every file type, I would look first at application code, database queries, thread pools, or request handling.&lt;/p&gt;

&lt;p&gt;But that was not the pattern.&lt;/p&gt;

&lt;p&gt;Small files were fine. Metadata-style operations were fine. The real pain showed up when the service had to deliver large MP4 files.&lt;/p&gt;

&lt;p&gt;That points to a different class of bottleneck:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;disk IO&lt;/li&gt;
&lt;li&gt;network throughput&lt;/li&gt;
&lt;li&gt;response streaming&lt;/li&gt;
&lt;li&gt;buffering behavior&lt;/li&gt;
&lt;li&gt;JVM memory pressure&lt;/li&gt;
&lt;li&gt;request thread occupancy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In other words, the backend was good at being an API. It was less ideal as a media delivery layer under heavy load.&lt;/p&gt;

&lt;h2&gt;
  
  
  CPU Usage During the Spring Boot Load Test
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjph6nfcd5kfopzbf1nbk.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjph6nfcd5kfopzbf1nbk.webp" alt="Spring Boot load test CPU usage chart" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CPU usage increased with traffic, but it did not look like the primary limiting factor.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;CPU usage increased as traffic increased, which is expected.&lt;/p&gt;

&lt;p&gt;But the average CPU usage stayed around &lt;strong&gt;35.4%&lt;/strong&gt; during the test. That suggests the service was not purely compute-bound.&lt;/p&gt;

&lt;p&gt;If CPU had been pinned near 100%, I would look first at expensive transformations, compression, serialization, hashing, or application-level processing.&lt;/p&gt;

&lt;p&gt;Here, the more likely pressure was IO and streaming large responses.&lt;/p&gt;

&lt;p&gt;That is common in file systems. The bottleneck often moves away from "Can my controller handle the request?" and toward "Should this application server be responsible for pushing large media bytes at all?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory Usage During the Performance Test
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddcy4fjo0dc1iyzmxq4u.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fddcy4fjo0dc1iyzmxq4u.webp" alt="Spring Boot load test memory usage chart" width="800" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Memory usage rose during heavier stages but stayed within a manageable range during this test.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Memory usage rose during heavier stages of the test and peaked around &lt;strong&gt;126 MB&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That does not prove a memory leak.&lt;/p&gt;

&lt;p&gt;It does show that large file delivery creates more pressure than simple metadata requests. Depending on how files are read, buffered, streamed, cached, or proxied, memory can become a bigger concern as concurrency rises.&lt;/p&gt;

&lt;p&gt;For production workloads, I would test this again with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;larger files&lt;/li&gt;
&lt;li&gt;longer-running traffic&lt;/li&gt;
&lt;li&gt;more concurrent downloads&lt;/li&gt;
&lt;li&gt;different JVM heap settings&lt;/li&gt;
&lt;li&gt;object storage or CDN-backed delivery&lt;/li&gt;
&lt;li&gt;request patterns that mimic real users more closely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A short ramp test is useful, but it is not the same thing as a full soak test.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Spring Boot Performance Test Shows
&lt;/h2&gt;

&lt;p&gt;My main takeaway is not "Spring Boot is slow."&lt;/p&gt;

&lt;p&gt;The better takeaway is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Spring Boot handled the API workload well. The scaling concern was using the application server as the primary delivery path for large media files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is an architecture issue more than a framework issue.&lt;/p&gt;

&lt;p&gt;Spring Boot is a strong fit for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication&lt;/li&gt;
&lt;li&gt;authorization&lt;/li&gt;
&lt;li&gt;metadata management&lt;/li&gt;
&lt;li&gt;file ownership rules&lt;/li&gt;
&lt;li&gt;upload orchestration&lt;/li&gt;
&lt;li&gt;audit logging&lt;/li&gt;
&lt;li&gt;business workflows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But for high-volume file downloads, especially videos, I would usually move delivery away from the application server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Architecture Recommendations
&lt;/h2&gt;

&lt;p&gt;For a production-grade version of this architecture, I would separate application logic from file delivery.&lt;/p&gt;

&lt;p&gt;A more scalable design would usually look like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring Boot handles auth, metadata, permissions, and business rules&lt;/li&gt;
&lt;li&gt;files live in S3-compatible object storage such as AWS S3 or Cloudflare R2&lt;/li&gt;
&lt;li&gt;downloads use signed URLs&lt;/li&gt;
&lt;li&gt;public or semi-public assets go through a CDN&lt;/li&gt;
&lt;li&gt;very large files support efficient streaming and range requests&lt;/li&gt;
&lt;li&gt;the backend avoids becoming the main bandwidth bottleneck&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on the use case, Nginx static serving or internal redirects can also be useful. But for cloud-native systems, object storage plus signed URLs is usually the cleaner path.&lt;/p&gt;

&lt;p&gt;The key idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let Spring Boot decide who can access a file. Do not force Spring Boot to personally deliver every large byte forever.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Practical Lessons From the Test
&lt;/h2&gt;

&lt;p&gt;A few lessons stood out from this benchmark.&lt;/p&gt;

&lt;p&gt;First, zero failures does not mean the system is healthy at every load level. A service can keep returning 200 responses while the user experience gets worse.&lt;/p&gt;

&lt;p&gt;Second, P95 latency is more useful than average latency for this kind of system. File workloads tend to have uneven response times, especially when file sizes vary.&lt;/p&gt;

&lt;p&gt;Third, endpoint-level metrics are not enough. File-type metrics told the real story here.&lt;/p&gt;

&lt;p&gt;Fourth, performance testing should influence architecture. The answer is not always "tune the JVM" or "add more instances." Sometimes the answer is "this server should not be doing this job."&lt;/p&gt;

&lt;h2&gt;
  
  
  Need Production-Grade File Infrastructure?
&lt;/h2&gt;

&lt;p&gt;This benchmark was conducted on FiloraFS-Lite, a lightweight starter built for experimentation and rapid setup.&lt;/p&gt;

&lt;p&gt;If you need JWT auth, S3 storage, secure file access, streaming, and a production-ready architecture, FiloraFS-Pro is the next step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/boilerplates/filora-fs-pro/" rel="noopener noreferrer"&gt;Explore FiloraFS-Pro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built for real production workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The backend survived the full ramp to 7,500 RPM without HTTP failures, which is a good stability signal.&lt;/p&gt;

&lt;p&gt;But the test also showed where the architecture starts to bend: large media delivery.&lt;/p&gt;

&lt;p&gt;That is the real value of performance testing. It is not just about proving the app can handle traffic. It is about finding the point where an architectural decision becomes a bottleneck.&lt;/p&gt;

&lt;p&gt;For this system, the next step is clear: keep Spring Boot in charge of API logic, but move heavy file delivery to infrastructure designed for it.&lt;/p&gt;

&lt;p&gt;If you are building a production Spring Boot file upload system, that separation will matter more and more as your file sizes and traffic grow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/" rel="noopener noreferrer"&gt;Spring Boot File Upload (Production Guide): Secure, Scalable System Design&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Building file upload is easy. Making it production-ready is not. Learn security, scalable storage, and architecture patterns for real workloads.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://buildbasekit.com/blogs/local-storage-vs-s3-spring-boot/" rel="noopener noreferrer"&gt;Local Storage vs S3 for Spring Boot File Uploads&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;This benchmark exposed local file serving limits. Learn when local storage stops making sense and when S3 becomes the better architecture choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://buildbasekit.com/blogs/s3-presigned-url-spring-boot/" rel="noopener noreferrer"&gt;S3 Pre-Signed URLs in Spring Boot for Secure File Access&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If direct file serving became your bottleneck, pre-signed URLs are one of the cleanest ways to offload downloads from your backend.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Spring Boot Large File Upload (Limits, Streaming, Best Practices)</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Mon, 11 May 2026 12:23:50 +0000</pubDate>
      <link>https://dev.to/buildbasekit/spring-boot-large-file-upload-limits-streaming-best-practices-bc4</link>
      <guid>https://dev.to/buildbasekit/spring-boot-large-file-upload-limits-streaming-best-practices-bc4</guid>
      <description>&lt;p&gt;Large file uploads work fine in development.&lt;/p&gt;

&lt;p&gt;Then somebody uploads a 2GB video in production and suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory spikes&lt;/li&gt;
&lt;li&gt;requests hang&lt;/li&gt;
&lt;li&gt;uploads fail&lt;/li&gt;
&lt;li&gt;your server becomes unstable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A lot of Spring Boot file upload tutorials only show this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/upload"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MultipartFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Uploaded"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works for small files.&lt;/p&gt;

&lt;p&gt;It is not enough for production systems.&lt;/p&gt;

&lt;p&gt;In this guide, I’ll show how to handle large file uploads in Spring Boot properly using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;upload limits&lt;/li&gt;
&lt;li&gt;streaming&lt;/li&gt;
&lt;li&gt;clean storage structure&lt;/li&gt;
&lt;li&gt;validation&lt;/li&gt;
&lt;li&gt;production-safe practices&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Real Problem with Large File Uploads
&lt;/h2&gt;

&lt;p&gt;Small uploads hide architectural problems.&lt;/p&gt;

&lt;p&gt;Large uploads expose them immediately.&lt;/p&gt;

&lt;p&gt;Typical issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;loading entire files into memory&lt;/li&gt;
&lt;li&gt;no upload size limits&lt;/li&gt;
&lt;li&gt;blocking request threads&lt;/li&gt;
&lt;li&gt;slow local disk operations&lt;/li&gt;
&lt;li&gt;poor validation&lt;/li&gt;
&lt;li&gt;timeout failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Never let file uploads overload your application memory.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How Large File Uploads Should Work
&lt;/h2&gt;

&lt;p&gt;A clean upload flow usually looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client sends multipart request&lt;/li&gt;
&lt;li&gt;Spring Boot validates request size&lt;/li&gt;
&lt;li&gt;File is streamed instead of fully buffered&lt;/li&gt;
&lt;li&gt;Storage layer handles persistence&lt;/li&gt;
&lt;li&gt;API returns file reference or metadata&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That separation matters.&lt;/p&gt;

&lt;p&gt;Your controller should not contain storage logic.&lt;/p&gt;

&lt;p&gt;Your storage layer should not know about HTTP requests.&lt;/p&gt;

&lt;p&gt;Keep upload architecture clean from the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Configure Upload Limits First
&lt;/h2&gt;

&lt;p&gt;One of the biggest mistakes is running without limits.&lt;/p&gt;

&lt;p&gt;Set both:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;max file size&lt;/li&gt;
&lt;li&gt;max request size&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;spring.servlet.multipart.max-file-size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;500MB&lt;/span&gt;
&lt;span class="py"&gt;spring.servlet.multipart.max-request-size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;500MB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents unexpected memory pressure and protects your server from oversized uploads.&lt;/p&gt;

&lt;p&gt;Without limits, somebody can accidentally (or intentionally) upload files large enough to crash your application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Avoid Loading Large Files into Memory
&lt;/h2&gt;

&lt;p&gt;This is where many implementations fail.&lt;/p&gt;

&lt;p&gt;Bad approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That loads the full file into memory.&lt;/p&gt;

&lt;p&gt;For large uploads, this becomes dangerous very quickly.&lt;/p&gt;

&lt;p&gt;Better approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use streams&lt;/li&gt;
&lt;li&gt;process incrementally&lt;/li&gt;
&lt;li&gt;avoid unnecessary buffering&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Minimal Streaming Upload Example
&lt;/h2&gt;

&lt;p&gt;Here’s a simple starting point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/upload"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@RequestParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MultipartFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File is empty"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"Uploaded: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOriginalFilename&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example is intentionally minimal.&lt;/p&gt;

&lt;p&gt;In production systems you should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;stream uploads&lt;/li&gt;
&lt;li&gt;move storage outside application memory&lt;/li&gt;
&lt;li&gt;separate upload service from controller&lt;/li&gt;
&lt;li&gt;use external storage for scalability&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Use a Proper Storage Strategy
&lt;/h2&gt;

&lt;p&gt;Storing everything locally works initially.&lt;/p&gt;

&lt;p&gt;It becomes painful later.&lt;/p&gt;

&lt;p&gt;Especially when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;files become large&lt;/li&gt;
&lt;li&gt;traffic increases&lt;/li&gt;
&lt;li&gt;you deploy multiple instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A better long-term approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep upload logic separate&lt;/li&gt;
&lt;li&gt;abstract storage behind services&lt;/li&gt;
&lt;li&gt;move to cloud storage when needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Typical production options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS S3&lt;/li&gt;
&lt;li&gt;Cloudflare R2&lt;/li&gt;
&lt;li&gt;MinIO&lt;/li&gt;
&lt;li&gt;Google Cloud Storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important part is structure, not the provider.&lt;/p&gt;




&lt;h2&gt;
  
  
  Validate Uploads Early
&lt;/h2&gt;

&lt;p&gt;Validation matters more with large files.&lt;/p&gt;

&lt;p&gt;Always validate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file size&lt;/li&gt;
&lt;li&gt;file type&lt;/li&gt;
&lt;li&gt;empty uploads&lt;/li&gt;
&lt;li&gt;malformed requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do validation before expensive processing starts.&lt;/p&gt;

&lt;p&gt;Do not trust client-side validation alone.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes I Keep Seeing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. No Upload Limits
&lt;/h3&gt;

&lt;p&gt;This is risky even for internal systems.&lt;/p&gt;

&lt;p&gt;Always configure limits.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Using &lt;code&gt;file.getBytes()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is one of the fastest ways to create memory problems.&lt;/p&gt;

&lt;p&gt;Prefer streams.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Mixing Upload and Storage Logic
&lt;/h3&gt;

&lt;p&gt;Controllers become messy very quickly.&lt;/p&gt;

&lt;p&gt;Keep storage logic inside dedicated services.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Ignoring Failed Upload Handling
&lt;/h3&gt;

&lt;p&gt;Uploads fail in real systems.&lt;/p&gt;

&lt;p&gt;Handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;partial uploads&lt;/li&gt;
&lt;li&gt;network interruptions&lt;/li&gt;
&lt;li&gt;storage failures&lt;/li&gt;
&lt;li&gt;cleanup logic&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Without vs With Proper Handling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without Proper Handling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;files fully loaded into memory&lt;/li&gt;
&lt;li&gt;unstable performance&lt;/li&gt;
&lt;li&gt;higher crash risk&lt;/li&gt;
&lt;li&gt;poor scalability&lt;/li&gt;
&lt;li&gt;difficult maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With Proper Handling
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;streaming-based uploads&lt;/li&gt;
&lt;li&gt;predictable memory usage&lt;/li&gt;
&lt;li&gt;scalable storage architecture&lt;/li&gt;
&lt;li&gt;cleaner backend structure&lt;/li&gt;
&lt;li&gt;safer production behavior&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Large file uploads are not difficult.&lt;/p&gt;

&lt;p&gt;But they do require intentional architecture.&lt;/p&gt;

&lt;p&gt;The biggest improvements usually come from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;setting limits&lt;/li&gt;
&lt;li&gt;avoiding memory-heavy processing&lt;/li&gt;
&lt;li&gt;streaming correctly&lt;/li&gt;
&lt;li&gt;separating storage responsibilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start simple.&lt;/p&gt;

&lt;p&gt;But design the upload system in a way that can scale later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Production-Ready Spring Boot File Upload Boilerplate
&lt;/h2&gt;

&lt;p&gt;If you want a production-ready starting point instead of building everything from scratch:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring Boot file upload backend&lt;/li&gt;
&lt;li&gt;clean architecture&lt;/li&gt;
&lt;li&gt;validation structure&lt;/li&gt;
&lt;li&gt;scalable upload flow&lt;/li&gt;
&lt;li&gt;storage-ready setup&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Spring Boot File Upload API (Clean Structure Guide)&lt;br&gt;
&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-api/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-api/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Spring Boot File Upload Mistakes (Common Issues)&lt;br&gt;
&lt;a href="https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upload Files to AWS S3 with Spring Boot&lt;br&gt;
&lt;a href="https://buildbasekit.com/blogs/aws-s3-file-upload-spring-boot/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/aws-s3-file-upload-spring-boot/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I’m crash testing FiloraFS-Lite under load (p95, pressure, failures)</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Mon, 27 Apr 2026 08:59:11 +0000</pubDate>
      <link>https://dev.to/buildbasekit/im-crash-testing-filorafs-lite-under-load-p95-pressure-failures-24lj</link>
      <guid>https://dev.to/buildbasekit/im-crash-testing-filorafs-lite-under-load-p95-pressure-failures-24lj</guid>
      <description>&lt;p&gt;I started running crash tests on &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;FiloraFS-Lite&lt;/a&gt; to see how it actually behaves under pressure.&lt;/p&gt;

&lt;p&gt;Not benchmarks. Not ideal conditions.&lt;br&gt;
Real stress.&lt;/p&gt;

&lt;p&gt;The focus is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;where it starts breaking&lt;/li&gt;
&lt;li&gt;how early signals show up (p95, pressure)&lt;/li&gt;
&lt;li&gt;what fails first under sustained load&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I’m testing right now
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;increasing RPM until the system shows pressure&lt;/li&gt;
&lt;li&gt;tracking how latency (p95) degrades&lt;/li&gt;
&lt;li&gt;observing write pressure under continuous load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I already have logs from initial runs.&lt;/p&gt;

&lt;p&gt;But instead of rushing conclusions, I’m validating signals properly before sharing anything.&lt;/p&gt;

&lt;p&gt;No assumptions. Just observed behavior.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters
&lt;/h2&gt;

&lt;p&gt;Small-scale tests looked fine.&lt;/p&gt;

&lt;p&gt;But real systems don’t fail in clean scenarios.&lt;/p&gt;

&lt;p&gt;They fail when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;load spikes&lt;/li&gt;
&lt;li&gt;resources get constrained&lt;/li&gt;
&lt;li&gt;edge cases stack together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s the environment I’m trying to simulate.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I’ll share next
&lt;/h2&gt;

&lt;p&gt;Once analysis is done, I’ll publish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what broke first&lt;/li&gt;
&lt;li&gt;early warning signals&lt;/li&gt;
&lt;li&gt;what actually mattered vs noise&lt;/li&gt;
&lt;li&gt;what needs to change&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you’ve done similar crash or stress testing:&lt;/p&gt;

&lt;p&gt;What signal usually shows up first for you under load?&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>performance</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Setting Up Auth in 2026: AI Is Fast, But Boilerplates Still Win</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Sat, 18 Apr 2026 16:47:50 +0000</pubDate>
      <link>https://dev.to/buildbasekit/setting-up-auth-in-2026-ai-is-fast-but-boilerplates-still-win-8of</link>
      <guid>https://dev.to/buildbasekit/setting-up-auth-in-2026-ai-is-fast-but-boilerplates-still-win-8of</guid>
      <description>&lt;p&gt;Let’s be honest.&lt;/p&gt;

&lt;p&gt;Auth is still one of the most frustrating parts of building a backend.&lt;/p&gt;

&lt;p&gt;Even in 2026.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Reality
&lt;/h2&gt;

&lt;p&gt;Setting up authentication today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;By yourself&lt;/strong&gt; → 8–15 hours
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With AI&lt;/strong&gt; → 2–3 hours
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With a good boilerplate&lt;/strong&gt; → 2–3 minutes
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI helped a lot.&lt;/p&gt;

&lt;p&gt;But it didn’t remove the hardest part.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Problem Isn’t Code
&lt;/h2&gt;

&lt;p&gt;Most devs think auth is about writing code.&lt;/p&gt;

&lt;p&gt;It’s not.&lt;/p&gt;

&lt;p&gt;It’s about decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT or session?&lt;/li&gt;
&lt;li&gt;Refresh tokens?&lt;/li&gt;
&lt;li&gt;Role system?&lt;/li&gt;
&lt;li&gt;Multi-tenant or not?&lt;/li&gt;
&lt;li&gt;Middleware structure?&lt;/li&gt;
&lt;li&gt;Edge cases?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI can generate code fast.&lt;/p&gt;

&lt;p&gt;But it &lt;strong&gt;doesn’t give you a clean, reliable system&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So you still spend hours:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prompting
&lt;/li&gt;
&lt;li&gt;fixing inconsistencies
&lt;/li&gt;
&lt;li&gt;restructuring code
&lt;/li&gt;
&lt;li&gt;debugging flows you didn’t design
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s where the time goes.&lt;/p&gt;




&lt;h2&gt;
  
  
  What AI Actually Solves
&lt;/h2&gt;

&lt;p&gt;AI is great at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generating endpoints
&lt;/li&gt;
&lt;li&gt;writing controllers
&lt;/li&gt;
&lt;li&gt;suggesting schemas
&lt;/li&gt;
&lt;li&gt;speeding up repetitive work
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It reduces typing.&lt;/p&gt;

&lt;p&gt;But typing was never the bottleneck.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Boilerplates Win
&lt;/h2&gt;

&lt;p&gt;A good boilerplate removes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;decision fatigue
&lt;/li&gt;
&lt;li&gt;architecture mistakes
&lt;/li&gt;
&lt;li&gt;incomplete flows
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;working auth system
&lt;/li&gt;
&lt;li&gt;clean structure
&lt;/li&gt;
&lt;li&gt;tested flows
&lt;/li&gt;
&lt;li&gt;production-ready defaults
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And most importantly:&lt;/p&gt;

&lt;p&gt;👉 everything already fits together&lt;/p&gt;

&lt;p&gt;No guessing. No patching.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI + Boilerplate &amp;gt; AI Alone
&lt;/h2&gt;

&lt;p&gt;This is the key shift.&lt;/p&gt;

&lt;p&gt;Boilerplates don’t compete with AI.&lt;/p&gt;

&lt;p&gt;They &lt;strong&gt;make AI usable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;With a boilerplate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI works inside a solid structure
&lt;/li&gt;
&lt;li&gt;You extend instead of rebuild
&lt;/li&gt;
&lt;li&gt;Less debugging, fewer surprises
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI gives fragments
&lt;/li&gt;
&lt;li&gt;You connect everything
&lt;/li&gt;
&lt;li&gt;Things break in subtle ways
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Quick Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without Boilerplate
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Generate auth with AI
&lt;/li&gt;
&lt;li&gt;Fix token logic
&lt;/li&gt;
&lt;li&gt;Add middleware
&lt;/li&gt;
&lt;li&gt;Handle refresh flow
&lt;/li&gt;
&lt;li&gt;Patch security gaps
&lt;/li&gt;
&lt;li&gt;Refactor structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; 2–3 hours (best case)&lt;/p&gt;




&lt;h3&gt;
  
  
  With Boilerplate
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Clone repo
&lt;/li&gt;
&lt;li&gt;Add env values
&lt;/li&gt;
&lt;li&gt;Start server
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; 2–3 minutes&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Shift in 2026
&lt;/h2&gt;

&lt;p&gt;It’s no longer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can you write code fast?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can you start from the right foundation?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI speeds up execution
&lt;/li&gt;
&lt;li&gt;Boilerplates remove setup
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And setup is where most time is lost.&lt;/p&gt;




&lt;h2&gt;
  
  
  When You Should Skip Boilerplates
&lt;/h2&gt;

&lt;p&gt;You don’t need one if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you’re learning auth deeply
&lt;/li&gt;
&lt;li&gt;your project is experimental
&lt;/li&gt;
&lt;li&gt;you enjoy building infra repeatedly
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Otherwise, you’re wasting time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;AI made coding faster.&lt;/p&gt;

&lt;p&gt;But it didn’t solve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;architecture
&lt;/li&gt;
&lt;li&gt;structure
&lt;/li&gt;
&lt;li&gt;system design
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why boilerplates still win.&lt;/p&gt;

&lt;p&gt;Not because they save minutes.&lt;/p&gt;

&lt;p&gt;Because they remove hours of thinking.&lt;/p&gt;




&lt;h2&gt;
  
  
  Build Faster With a Real Starting Point
&lt;/h2&gt;

&lt;p&gt;If you’re tired of rebuilding auth every time,&lt;br&gt;&lt;br&gt;
start with something that already works.&lt;/p&gt;

&lt;p&gt;👉 Check out &lt;a href="https://buildbasekit.com/" rel="noopener noreferrer"&gt;BuildBaseKit&lt;/a&gt; (ready-to-use backend starters)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth already set up
&lt;/li&gt;
&lt;li&gt;Clean structure&lt;/li&gt;
&lt;li&gt;RBAC with JWT&lt;/li&gt;
&lt;li&gt;Extend, don’t rebuild
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>backend</category>
      <category>programming</category>
      <category>startup</category>
    </item>
    <item>
      <title>Stop Wasting Weeks on Backend Setup</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Mon, 13 Apr 2026 16:53:37 +0000</pubDate>
      <link>https://dev.to/buildbasekit/stop-wasting-weeks-on-backend-setup-1b81</link>
      <guid>https://dev.to/buildbasekit/stop-wasting-weeks-on-backend-setup-1b81</guid>
      <description>&lt;p&gt;If you’ve built more than one backend, you’ve seen this before.&lt;/p&gt;

&lt;p&gt;You start a new idea.&lt;/p&gt;

&lt;p&gt;Then you spend days setting up the same things again:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auth&lt;/li&gt;
&lt;li&gt;Roles and permissions&lt;/li&gt;
&lt;li&gt;Multi-tenancy&lt;/li&gt;
&lt;li&gt;Database setup&lt;/li&gt;
&lt;li&gt;Basic APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And suddenly, a week is gone.&lt;/p&gt;

&lt;p&gt;No real product. No users.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real issue
&lt;/h2&gt;

&lt;p&gt;This work isn’t hard.&lt;/p&gt;

&lt;p&gt;It’s repetitive.&lt;/p&gt;

&lt;p&gt;You’re rebuilding solved problems instead of building something people care about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this slows you down
&lt;/h2&gt;

&lt;p&gt;Early stage is about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shipping fast&lt;/li&gt;
&lt;li&gt;getting feedback&lt;/li&gt;
&lt;li&gt;learning quickly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But instead, most time goes into setup.&lt;/p&gt;

&lt;p&gt;By the time you're ready, momentum is already low.&lt;/p&gt;

&lt;h2&gt;
  
  
  A mistake I kept repeating
&lt;/h2&gt;

&lt;p&gt;On one project, I spent around 10 days just setting up backend basics.&lt;/p&gt;

&lt;p&gt;When I finished, I had nothing to show.&lt;/p&gt;

&lt;p&gt;No users. No feedback.&lt;/p&gt;

&lt;p&gt;That’s when it clicked.&lt;/p&gt;

&lt;p&gt;I wasn’t building a product.&lt;/p&gt;

&lt;p&gt;I was rebuilding infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually matters
&lt;/h2&gt;

&lt;p&gt;You don’t need a perfect backend.&lt;/p&gt;

&lt;p&gt;You need something that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;works&lt;/li&gt;
&lt;li&gt;supports real users&lt;/li&gt;
&lt;li&gt;lets you move fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s it.&lt;/p&gt;

&lt;h2&gt;
  
  
  A better approach
&lt;/h2&gt;

&lt;p&gt;Start with a base that already handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;auth&lt;/li&gt;
&lt;li&gt;roles&lt;/li&gt;
&lt;li&gt;structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then focus on your actual product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reality check
&lt;/h2&gt;

&lt;p&gt;Most developers don’t fail because of bad architecture.&lt;/p&gt;

&lt;p&gt;They fail because they never ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;Next time you start a project, don’t ask:&lt;/p&gt;

&lt;p&gt;“What’s the best setup?”&lt;/p&gt;

&lt;p&gt;Ask:&lt;/p&gt;

&lt;p&gt;“How fast can I ship something useful?”&lt;/p&gt;




&lt;p&gt;If you want to skip backend setup and start faster, try this free:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://buildbasekit.com/boilerplates/authkit-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/authkit-lite/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>saas</category>
      <category>webdev</category>
    </item>
    <item>
      <title>JWT vs Session Authentication in Spring Boot: Which One Should You Use?</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Thu, 09 Apr 2026 12:07:25 +0000</pubDate>
      <link>https://dev.to/buildbasekit/jwt-vs-session-authentication-in-spring-boot-which-one-should-you-use-4gj5</link>
      <guid>https://dev.to/buildbasekit/jwt-vs-session-authentication-in-spring-boot-which-one-should-you-use-4gj5</guid>
      <description>&lt;p&gt;Most developers pick JWT or sessions based on tutorials or trends.&lt;/p&gt;

&lt;p&gt;That is a mistake.&lt;/p&gt;

&lt;p&gt;The wrong choice can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;break scalability
&lt;/li&gt;
&lt;li&gt;increase complexity
&lt;/li&gt;
&lt;li&gt;create security issues later
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide explains how both actually work in real systems and how to choose the right one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Answer
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;JWT&lt;/strong&gt; for APIs, microservices, and scalable systems
&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;sessions&lt;/strong&gt; for simple server rendered applications
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JWT is stateless and scalable&lt;br&gt;&lt;br&gt;
Sessions are simpler but harder to scale  &lt;/p&gt;




&lt;h2&gt;
  
  
  When This Decision Matters
&lt;/h2&gt;

&lt;p&gt;You need to choose carefully if you are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;building REST APIs
&lt;/li&gt;
&lt;li&gt;creating login systems
&lt;/li&gt;
&lt;li&gt;scaling across multiple servers
&lt;/li&gt;
&lt;li&gt;deciding between stateless vs stateful architecture
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing wrong early creates pain later.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Session Based Authentication
&lt;/h2&gt;

&lt;p&gt;Session authentication stores user state on the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;server creates a session after login
&lt;/li&gt;
&lt;li&gt;session data is stored on server
&lt;/li&gt;
&lt;li&gt;client sends session ID with each request
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key characteristics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;stateful
&lt;/li&gt;
&lt;li&gt;simple to implement
&lt;/li&gt;
&lt;li&gt;tightly coupled to server
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Problem:
&lt;/h3&gt;

&lt;p&gt;Scaling requires shared storage like Redis.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is JWT Authentication
&lt;/h2&gt;

&lt;p&gt;JWT is a stateless authentication method.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;server generates a token
&lt;/li&gt;
&lt;li&gt;client stores token
&lt;/li&gt;
&lt;li&gt;token is sent with every request
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key characteristics:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;stateless
&lt;/li&gt;
&lt;li&gt;no server side storage
&lt;/li&gt;
&lt;li&gt;works well across services
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tradeoff:
&lt;/h3&gt;

&lt;p&gt;Token expiration and revocation are harder.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Differences
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Session&lt;/th&gt;
&lt;th&gt;JWT&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Stateful&lt;/td&gt;
&lt;td&gt;Stateless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalability&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best For&lt;/td&gt;
&lt;td&gt;Monolith apps&lt;/td&gt;
&lt;td&gt;APIs and microservices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  When to Use Session Authentication
&lt;/h2&gt;

&lt;p&gt;Use sessions if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you are building server rendered apps
&lt;/li&gt;
&lt;li&gt;your system is small or medium scale
&lt;/li&gt;
&lt;li&gt;you want simple session invalidation
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sessions are easier to manage but not built for scale.&lt;/p&gt;




&lt;h2&gt;
  
  
  When to Use JWT Authentication
&lt;/h2&gt;

&lt;p&gt;Use JWT if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you are building REST APIs
&lt;/li&gt;
&lt;li&gt;frontend and backend are separate
&lt;/li&gt;
&lt;li&gt;you need scalability
&lt;/li&gt;
&lt;li&gt;you are using microservices
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JWT fits modern backend architecture better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Authentication Flow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Session Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;user logs in
&lt;/li&gt;
&lt;li&gt;server creates session
&lt;/li&gt;
&lt;li&gt;session ID stored in cookie
&lt;/li&gt;
&lt;li&gt;server validates session on every request
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  JWT Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;user logs in
&lt;/li&gt;
&lt;li&gt;server generates token
&lt;/li&gt;
&lt;li&gt;client stores token
&lt;/li&gt;
&lt;li&gt;token sent with each request
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Example: JWT Filter in Spring Boot
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JwtFilter&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;OncePerRequestFilter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doFilterInternal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpServletRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                    &lt;span class="nc"&gt;HttpServletResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                                    &lt;span class="nc"&gt;FilterChain&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// extract token&lt;/span&gt;
        &lt;span class="c1"&gt;// validate token&lt;/span&gt;
        &lt;span class="c1"&gt;// set authentication&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;using JWT without handling token expiration&lt;/li&gt;
&lt;li&gt;using sessions in distributed systems without shared storage&lt;/li&gt;
&lt;li&gt;choosing based on trends instead of requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These mistakes cause issues in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Guides
&lt;/h2&gt;

&lt;p&gt;If you want implementation details:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/blogs/spring-boot-jwt-authentication/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-jwt-authentication/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For real backend architecture:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;There is no universal best option.&lt;/p&gt;

&lt;p&gt;JWT is better for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APIs&lt;/li&gt;
&lt;li&gt;distributed systems&lt;/li&gt;
&lt;li&gt;scalable backends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sessions are better for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple apps&lt;/li&gt;
&lt;li&gt;quick setups&lt;/li&gt;
&lt;li&gt;server rendered systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choose based on your architecture, not trends.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is JWT more secure than sessions?
&lt;/h3&gt;

&lt;p&gt;No. Security depends on implementation, not the method.&lt;/p&gt;




&lt;h3&gt;
  
  
  Can JWT be revoked?
&lt;/h3&gt;

&lt;p&gt;Yes, but you need extra mechanisms like token blacklisting.&lt;/p&gt;




&lt;h3&gt;
  
  
  Should I always use JWT for APIs?
&lt;/h3&gt;

&lt;p&gt;Not always. Simpler systems may work better with sessions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Build Faster
&lt;/h2&gt;

&lt;p&gt;If you want a ready to use authentication setup:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/authkit-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/authkit-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JWT authentication&lt;/li&gt;
&lt;li&gt;role based access&lt;/li&gt;
&lt;li&gt;clean architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free and production ready.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>backend</category>
      <category>security</category>
    </item>
    <item>
      <title>How to Store and Serve Files in Spring Boot (Local Storage Guide)</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Thu, 09 Apr 2026 11:45:48 +0000</pubDate>
      <link>https://dev.to/buildbasekit/how-to-store-and-serve-files-in-spring-boot-local-storage-guide-23id</link>
      <guid>https://dev.to/buildbasekit/how-to-store-and-serve-files-in-spring-boot-local-storage-guide-23id</guid>
      <description>&lt;p&gt;Storing files in Spring Boot using local storage is easy.&lt;/p&gt;

&lt;p&gt;But most implementations break when the project grows.&lt;/p&gt;

&lt;p&gt;What starts as a simple upload API quickly turns into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;messy file paths
&lt;/li&gt;
&lt;li&gt;poor structure
&lt;/li&gt;
&lt;li&gt;scaling issues
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide shows how to implement &lt;strong&gt;local file storage properly&lt;/strong&gt;, so your system stays clean and easy to upgrade later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Insight
&lt;/h2&gt;

&lt;p&gt;Local storage works well for small to medium applications.&lt;/p&gt;

&lt;p&gt;But it does not scale in distributed systems.&lt;/p&gt;

&lt;p&gt;Use it for simplicity, not long term architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Local File Storage in Spring Boot
&lt;/h2&gt;

&lt;p&gt;Local storage means saving uploaded files directly on your server filesystem instead of using cloud storage like S3.&lt;/p&gt;

&lt;p&gt;It is simple, fast, and easy to set up.&lt;/p&gt;




&lt;h2&gt;
  
  
  When You Should Use Local Storage
&lt;/h2&gt;

&lt;p&gt;Local storage is a good choice when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;building small to medium applications
&lt;/li&gt;
&lt;li&gt;creating internal tools or admin panels
&lt;/li&gt;
&lt;li&gt;working on MVPs or prototypes
&lt;/li&gt;
&lt;li&gt;running on a single server
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many projects start here. The key is to structure it properly from the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Basic File Storage Flow
&lt;/h2&gt;

&lt;p&gt;A typical file handling flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;receive file via upload endpoint
&lt;/li&gt;
&lt;li&gt;validate file
&lt;/li&gt;
&lt;li&gt;save file to local directory
&lt;/li&gt;
&lt;li&gt;store file reference in database
&lt;/li&gt;
&lt;li&gt;serve file via API
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Example: File Upload API
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/upload"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="nc"&gt;MultipartFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"/uploads/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOriginalFilename&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transferTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Uploaded"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but it is not production ready yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where to Store Files
&lt;/h2&gt;

&lt;p&gt;Do not store files inside your source code folders.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use a dedicated directory&lt;/li&gt;
&lt;li&gt;make the path configurable&lt;/li&gt;
&lt;li&gt;keep storage separate from your codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/data/uploads/
/var/app/files/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Serving Files Through API
&lt;/h2&gt;

&lt;p&gt;Never expose raw file paths directly.&lt;/p&gt;

&lt;p&gt;Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a download endpoint&lt;/li&gt;
&lt;li&gt;stream files instead of loading fully in memory&lt;/li&gt;
&lt;li&gt;set correct content type and headers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps your system secure and efficient.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keep Storage Logic Separate
&lt;/h2&gt;

&lt;p&gt;Avoid putting everything inside controllers.&lt;/p&gt;

&lt;p&gt;Use a clean structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Controller → handles request and response&lt;/li&gt;
&lt;li&gt;Service → handles file logic&lt;/li&gt;
&lt;li&gt;Storage layer → interacts with filesystem&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes future migration to S3 much easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  File Naming and Validation
&lt;/h2&gt;

&lt;p&gt;Never trust user input.&lt;/p&gt;

&lt;p&gt;Always:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;generate unique file names&lt;/li&gt;
&lt;li&gt;validate file type&lt;/li&gt;
&lt;li&gt;enforce file size limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bad validation is a common security risk.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;hardcoding file paths&lt;/li&gt;
&lt;li&gt;using original file names directly&lt;/li&gt;
&lt;li&gt;skipping validation&lt;/li&gt;
&lt;li&gt;mixing storage logic with business logic&lt;/li&gt;
&lt;li&gt;serving files without access control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These issues show up later when scaling.&lt;/p&gt;




&lt;h2&gt;
  
  
  When Local Storage is Enough
&lt;/h2&gt;

&lt;p&gt;Local storage is perfectly fine for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;small to medium apps&lt;/li&gt;
&lt;li&gt;internal tools&lt;/li&gt;
&lt;li&gt;prototypes&lt;/li&gt;
&lt;li&gt;single server deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it becomes a limitation when you scale beyond one server.&lt;/p&gt;




&lt;h2&gt;
  
  
  Local Storage vs Cloud Storage
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Local Storage&lt;/th&gt;
&lt;th&gt;Cloud Storage (S3)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;simple setup&lt;/td&gt;
&lt;td&gt;scalable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;low cost&lt;/td&gt;
&lt;td&gt;highly available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;single server&lt;/td&gt;
&lt;td&gt;distributed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hard to scale&lt;/td&gt;
&lt;td&gt;easy to scale&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you expect growth, design your system so migration is easy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want Production Ready File Upload?
&lt;/h2&gt;

&lt;p&gt;If you want to move beyond local storage and build a scalable system:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Local storage is simple, but it still needs structure.&lt;/p&gt;

&lt;p&gt;If you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;separate concerns&lt;/li&gt;
&lt;li&gt;validate inputs&lt;/li&gt;
&lt;li&gt;design clean APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;you can avoid most common problems and upgrade your system later without rewriting everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Where should I store files in Spring Boot?
&lt;/h3&gt;

&lt;p&gt;Use a dedicated directory outside your source code and make it configurable.&lt;/p&gt;




&lt;h3&gt;
  
  
  Is local storage good for production?
&lt;/h3&gt;

&lt;p&gt;It works for small applications, but not for distributed systems.&lt;/p&gt;




&lt;h3&gt;
  
  
  When should I switch to S3?
&lt;/h3&gt;

&lt;p&gt;When you need scalability, multiple servers, or global access.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Guides
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-api/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-api/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-production-guide/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/jwt-mistakes-spring-boot/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/jwt-mistakes-spring-boot/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Build Faster
&lt;/h2&gt;

&lt;p&gt;If you are tired of rebuilding file upload logic:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file upload system&lt;/li&gt;
&lt;li&gt;local storage setup&lt;/li&gt;
&lt;li&gt;clean architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free and ready to use.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>backend</category>
      <category>fileupload</category>
    </item>
    <item>
      <title>Spring Boot File Upload: Production Ready System Design Guide</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Thu, 09 Apr 2026 11:41:18 +0000</pubDate>
      <link>https://dev.to/buildbasekit/spring-boot-file-upload-production-ready-system-design-guide-490l</link>
      <guid>https://dev.to/buildbasekit/spring-boot-file-upload-production-ready-system-design-guide-490l</guid>
      <description>&lt;p&gt;Building a file upload API in Spring Boot is easy.&lt;/p&gt;

&lt;p&gt;Building one that actually works in production is where things break.&lt;/p&gt;

&lt;p&gt;Most developers start with a simple controller and local storage. It works fine until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;files get large
&lt;/li&gt;
&lt;li&gt;traffic increases
&lt;/li&gt;
&lt;li&gt;security becomes a concern
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide breaks down what actually matters when designing a &lt;strong&gt;production ready file upload system&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Insight
&lt;/h2&gt;

&lt;p&gt;A real file upload system is not just about uploading files.&lt;/p&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;security&lt;/li&gt;
&lt;li&gt;scalable storage&lt;/li&gt;
&lt;li&gt;efficient delivery&lt;/li&gt;
&lt;li&gt;clean API structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Miss one of these, and things start failing in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Problems with Basic File Upload Systems
&lt;/h2&gt;

&lt;p&gt;Most implementations fail because they ignore real world constraints.&lt;/p&gt;

&lt;p&gt;Typical issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;large uploads crash the server
&lt;/li&gt;
&lt;li&gt;no validation leads to security risks
&lt;/li&gt;
&lt;li&gt;local storage fails in distributed setups
&lt;/li&gt;
&lt;li&gt;messy APIs make scaling painful
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have seen any of these, your system is not production ready yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. File Upload Security in Spring Boot
&lt;/h2&gt;

&lt;p&gt;Security is not optional.&lt;/p&gt;

&lt;p&gt;Every uploaded file should be treated as untrusted input.&lt;/p&gt;

&lt;h3&gt;
  
  
  What you must do:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;validate file type and size
&lt;/li&gt;
&lt;li&gt;restrict access using authentication (JWT or sessions)
&lt;/li&gt;
&lt;li&gt;never expose internal file paths
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have not set up auth yet, start here:&lt;br&gt;&lt;br&gt;
👉 &lt;a href="https://buildbasekit.com/blogs/spring-boot-jwt-authentication/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-jwt-authentication/&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Scalable File Storage Strategy (S3 and Cloud)
&lt;/h2&gt;

&lt;p&gt;Local storage works only in early stages.&lt;/p&gt;

&lt;p&gt;It breaks when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you scale horizontally
&lt;/li&gt;
&lt;li&gt;you deploy across multiple servers
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Better approach:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;use cloud storage like AWS S3
&lt;/li&gt;
&lt;li&gt;keep storage separate from application logic
&lt;/li&gt;
&lt;li&gt;design storage as a pluggable layer
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you flexibility without rewriting your system later.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. File Access and Delivery Optimization
&lt;/h2&gt;

&lt;p&gt;Serving files from your backend is a bottleneck.&lt;/p&gt;
&lt;h3&gt;
  
  
  Production approach:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;use pre signed URLs
&lt;/li&gt;
&lt;li&gt;stream files instead of loading in memory
&lt;/li&gt;
&lt;li&gt;use CDN for faster delivery
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This reduces load on your server and improves performance.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. File Metadata and Management
&lt;/h2&gt;

&lt;p&gt;Uploading files is only half the problem.&lt;/p&gt;

&lt;p&gt;You also need to manage them.&lt;/p&gt;
&lt;h3&gt;
  
  
  Store metadata like:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;file name
&lt;/li&gt;
&lt;li&gt;size
&lt;/li&gt;
&lt;li&gt;owner
&lt;/li&gt;
&lt;li&gt;storage location
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Support:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;listing files
&lt;/li&gt;
&lt;li&gt;deleting files
&lt;/li&gt;
&lt;li&gt;updating metadata
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without this, your system becomes hard to maintain.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Clean File Upload API Design
&lt;/h2&gt;

&lt;p&gt;Bad API design kills scalability.&lt;/p&gt;
&lt;h3&gt;
  
  
  Keep things separated:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;upload endpoint
&lt;/li&gt;
&lt;li&gt;download endpoint
&lt;/li&gt;
&lt;li&gt;file management endpoints
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Follow clean architecture:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;controller layer
&lt;/li&gt;
&lt;li&gt;service layer
&lt;/li&gt;
&lt;li&gt;storage abstraction
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Avoid mixing responsibilities.&lt;/p&gt;


&lt;h2&gt;
  
  
  Recommended Architecture
&lt;/h2&gt;

&lt;p&gt;A production ready system usually looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Client → Controller → Service → Storage Layer → Cloud (S3)
↓
Database (metadata)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key components:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Controller for request handling
&lt;/li&gt;
&lt;li&gt;Service for business logic
&lt;/li&gt;
&lt;li&gt;Storage abstraction (S3 or local)
&lt;/li&gt;
&lt;li&gt;Database for metadata
&lt;/li&gt;
&lt;li&gt;Authentication layer
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This keeps the system maintainable and scalable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes to Avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;storing files directly on the app server
&lt;/li&gt;
&lt;li&gt;skipping validation
&lt;/li&gt;
&lt;li&gt;mixing storage logic with business logic
&lt;/li&gt;
&lt;li&gt;serving files without optimization
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These mistakes are the reason most systems fail later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;A production ready file upload system is not about adding features.&lt;/p&gt;

&lt;p&gt;It is about building the right foundation.&lt;/p&gt;

&lt;p&gt;If you design it properly from the start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you avoid scaling issues
&lt;/li&gt;
&lt;li&gt;you reduce security risks
&lt;/li&gt;
&lt;li&gt;you save time later
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most developers underestimate this until they hit real world problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want a Ready to Use Solution?
&lt;/h2&gt;

&lt;p&gt;If you do not want to rebuild this system again and again:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-pro/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-pro/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;authentication setup
&lt;/li&gt;
&lt;li&gt;S3 integration
&lt;/li&gt;
&lt;li&gt;clean API structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built for real world applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Guides
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-api/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-upload-api/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-storage-local-guide/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/spring-boot-file-storage-local-guide/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/" rel="noopener noreferrer"&gt;https://buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>springboot</category>
      <category>backend</category>
      <category>java</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Stop Building Messy Discord Bots in Java</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Tue, 31 Mar 2026 08:59:54 +0000</pubDate>
      <link>https://dev.to/buildbasekit/stop-building-messy-discord-bots-in-java-5eng</link>
      <guid>https://dev.to/buildbasekit/stop-building-messy-discord-bots-in-java-5eng</guid>
      <description>&lt;p&gt;Building a Discord bot in Java is easy.&lt;/p&gt;

&lt;p&gt;Keeping it clean as it grows is the hard part.&lt;/p&gt;

&lt;p&gt;You start with a few commands, and then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;logic starts mixing with event handling
&lt;/li&gt;
&lt;li&gt;commands become hard to extend
&lt;/li&gt;
&lt;li&gt;small changes break multiple features
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve seen bots become messy very quickly.&lt;/p&gt;

&lt;p&gt;Here’s how to structure it properly from the beginning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Discord bots become hard to maintain
&lt;/h2&gt;

&lt;p&gt;Most bots start small.&lt;/p&gt;

&lt;p&gt;But as features grow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;commands get tightly coupled with logic
&lt;/li&gt;
&lt;li&gt;event handling becomes inconsistent
&lt;/li&gt;
&lt;li&gt;code duplication increases
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without structure, scaling becomes painful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Discord Bot Structure Matters for Scalability
&lt;/h2&gt;

&lt;p&gt;When structure is ignored early, even small changes require touching multiple parts of the codebase.&lt;/p&gt;

&lt;p&gt;That’s when development slows down.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;commands become tightly coupled with logic&lt;/li&gt;
&lt;li&gt;event handling becomes inconsistent&lt;/li&gt;
&lt;li&gt;features are harder to extend&lt;/li&gt;
&lt;li&gt;debugging takes longer than expected&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core Components of a Scalable Discord Bot (Java JDA)
&lt;/h2&gt;

&lt;p&gt;A scalable Discord bot should have clear separation between different responsibilities. Even a simple bot benefits from a structured approach.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;command layer to handle slash commands and inputs&lt;/li&gt;
&lt;li&gt;event listeners to react to Discord events&lt;/li&gt;
&lt;li&gt;service layer for business logic&lt;/li&gt;
&lt;li&gt;configuration layer for environment variables and setup&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How a Discord Bot Works in JDA (Step by Step)
&lt;/h2&gt;

&lt;p&gt;Understanding the flow helps you design a clean and predictable structure.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User triggers a command or event in Discord&lt;/li&gt;
&lt;li&gt;JDA listener receives the event&lt;/li&gt;
&lt;li&gt;Command handler processes input&lt;/li&gt;
&lt;li&gt;Service layer executes logic&lt;/li&gt;
&lt;li&gt;Response is sent back to Discord&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If these responsibilities are not clearly separated, your bot quickly becomes difficult to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best practice: separate commands and business logic
&lt;/h2&gt;

&lt;p&gt;A common mistake is putting all logic inside command handlers.&lt;/p&gt;

&lt;p&gt;It works at first, but becomes hard to maintain as features grow.&lt;/p&gt;

&lt;p&gt;A better approach is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;commands handle input and response&lt;/li&gt;
&lt;li&gt;services handle actual logic&lt;/li&gt;
&lt;li&gt;listeners react to events independently
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// bad: logic inside command&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CommandEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ping"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reply&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pong"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// business logic mixed here&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Design Your Discord Bot for Scalability and Extension
&lt;/h2&gt;

&lt;p&gt;Your bot should be easy to extend without rewriting existing code. This means organizing features in a modular way.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;group related commands into modules&lt;/li&gt;
&lt;li&gt;keep shared logic reusable&lt;/li&gt;
&lt;li&gt;avoid hardcoded values in logic&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Recommended project structure
&lt;/h2&gt;

&lt;p&gt;This structure keeps your Discord bot modular and easy to scale as features grow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
src/
 ├── commands/
 ├── listeners/
 ├── service/
 ├── config/
 └── utils/

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If you don’t want to set this up from scratch:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/basely/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/basely/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It includes a clean Discord bot structure built with JDA.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common mistakes to avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;putting all code in a single package&lt;/li&gt;
&lt;li&gt;mixing event handling with business logic&lt;/li&gt;
&lt;li&gt;no clear naming or structure for commands&lt;/li&gt;
&lt;li&gt;duplicating logic across different commands&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Common symptom
&lt;/h3&gt;

&lt;p&gt;Adding a new command requires modifying multiple parts of the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Without vs with proper structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;logic inside command handlers&lt;/li&gt;
&lt;li&gt;hard to scale features&lt;/li&gt;
&lt;li&gt;duplicate code&lt;/li&gt;
&lt;li&gt;messy event handling&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;clean separation of layers&lt;/li&gt;
&lt;li&gt;easy to extend commands&lt;/li&gt;
&lt;li&gt;reusable logic&lt;/li&gt;
&lt;li&gt;predictable architecture&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Discord bots don’t become complex because of features.&lt;/p&gt;

&lt;p&gt;They become complex because of poor structure.&lt;/p&gt;

&lt;p&gt;If you separate commands, events, and logic early, your bot stays easy to extend as it grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want a clean Discord bot setup without rebuilding everything?
&lt;/h2&gt;

&lt;p&gt;I built a minimal Java boilerplate using JDA with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;proper command and event separation
&lt;/li&gt;
&lt;li&gt;clean service layer
&lt;/li&gt;
&lt;li&gt;scalable structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/basely/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/basely/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use it as a starting point instead of building your bot structure from scratch.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related articles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/how-to-build-and-deploy-a-java-discord-bot-using-spring-boot/" rel="noopener noreferrer"&gt;How to Build and Deploy a Java Discord Bot Using Spring Boot&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build a production-ready Discord bot using Java 21, Spring Boot, and JDA. Includes project structure, deployment, and best practices.&lt;/p&gt;




</description>
      <category>java</category>
      <category>springboot</category>
      <category>discordbot</category>
      <category>jda</category>
    </item>
    <item>
      <title>Stop Making These File Upload Mistakes in Spring Boot</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Tue, 31 Mar 2026 08:43:15 +0000</pubDate>
      <link>https://dev.to/buildbasekit/stop-making-these-file-upload-mistakes-in-spring-boot-2hlb</link>
      <guid>https://dev.to/buildbasekit/stop-making-these-file-upload-mistakes-in-spring-boot-2hlb</guid>
      <description>&lt;p&gt;File upload in Spring Boot looks simple… until it starts breaking.&lt;/p&gt;

&lt;p&gt;At first, it’s just one endpoint.&lt;/p&gt;

&lt;p&gt;Then suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file logic spreads across your codebase
&lt;/li&gt;
&lt;li&gt;validation is inconsistent
&lt;/li&gt;
&lt;li&gt;storage becomes hard to change
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve seen this turn messy very quickly.&lt;/p&gt;

&lt;p&gt;Here are the most common mistakes and how to avoid them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why file upload implementations go wrong
&lt;/h2&gt;

&lt;p&gt;File upload is deceptively simple.&lt;/p&gt;

&lt;p&gt;But as soon as you add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;validation
&lt;/li&gt;
&lt;li&gt;storage
&lt;/li&gt;
&lt;li&gt;file retrieval
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the logic starts spreading across multiple layers.&lt;/p&gt;

&lt;p&gt;Without structure, it becomes hard to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  How File Upload Works in Spring Boot (Step by Step)
&lt;/h2&gt;

&lt;p&gt;Understanding the flow helps you avoid most implementation mistakes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client sends file using multipart request&lt;/li&gt;
&lt;li&gt;Controller receives the file&lt;/li&gt;
&lt;li&gt;Service validates and processes it&lt;/li&gt;
&lt;li&gt;Storage layer saves the file&lt;/li&gt;
&lt;li&gt;API returns file reference or URL&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  1. Mixing file handling logic in controllers
&lt;/h3&gt;

&lt;p&gt;A common mistake is putting file processing directly inside controllers.&lt;/p&gt;

&lt;p&gt;It works at first, but quickly becomes hard to maintain.&lt;/p&gt;

&lt;p&gt;Common issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file saving logic inside endpoints
&lt;/li&gt;
&lt;li&gt;manual path handling in controllers
&lt;/li&gt;
&lt;li&gt;duplicate logic across APIs
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Controllers should stay thin. File handling belongs in a service layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. No validation for file size and type
&lt;/h2&gt;

&lt;p&gt;Accepting any file without validation can lead to security risks and performance issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uploading extremely large files&lt;/li&gt;
&lt;li&gt;accepting unsupported file types&lt;/li&gt;
&lt;li&gt;no limits configured for uploads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always define clear limits and validate file types before storing them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommended Spring Boot File Upload Structure
&lt;/h2&gt;

&lt;p&gt;This structure keeps file upload logic modular and easy to extend as requirements grow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
src/
 ├── controller/
 ├── service/
 ├── storage/
 ├── model/
 └── config/

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If you don’t want to build this structure from scratch:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It already includes a clean file upload setup with proper separation.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Hardcoding file paths
&lt;/h2&gt;

&lt;p&gt;Hardcoded paths make your application difficult to configure and deploy across environments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fixed local directories in code&lt;/li&gt;
&lt;li&gt;no environment-based configuration&lt;/li&gt;
&lt;li&gt;difficult to switch storage later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use configuration properties or environment variables for file storage paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common symptom
&lt;/h3&gt;

&lt;p&gt;You start with one upload endpoint and end up debugging file handling issues across multiple parts of your application.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. No clear storage abstraction
&lt;/h2&gt;

&lt;p&gt;Many implementations tightly couple file upload logic with storage details. This makes it hard to switch from local storage to cloud.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;storage logic mixed with upload logic&lt;/li&gt;
&lt;li&gt;no abstraction layer for storage&lt;/li&gt;
&lt;li&gt;difficult to extend to S3 or other providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A separate storage layer makes your system flexible and easier to maintain.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Ignoring file naming strategy
&lt;/h2&gt;

&lt;p&gt;Saving files with original names can cause conflicts and unexpected overwrites.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;duplicate file names overwrite existing files&lt;/li&gt;
&lt;li&gt;no unique identifiers&lt;/li&gt;
&lt;li&gt;hard to track files reliably&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use unique naming strategies such as UUIDs to avoid collisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Without vs with proper structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;file logic inside controllers&lt;/li&gt;
&lt;li&gt;hardcoded paths&lt;/li&gt;
&lt;li&gt;no validation&lt;/li&gt;
&lt;li&gt;difficult to scale&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  With structure
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;clean separation of layers&lt;/li&gt;
&lt;li&gt;flexible storage system&lt;/li&gt;
&lt;li&gt;proper validation&lt;/li&gt;
&lt;li&gt;easy to maintain&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;File upload is not the problem.&lt;/p&gt;

&lt;p&gt;Bad structure is.&lt;/p&gt;

&lt;p&gt;If you separate responsibilities and handle validation and storage properly, your system stays clean as it grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want a clean file upload setup without rebuilding it every time?
&lt;/h2&gt;

&lt;p&gt;I built a minimal Spring Boot boilerplate with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;proper service and storage separation
&lt;/li&gt;
&lt;li&gt;file upload and download endpoints
&lt;/li&gt;
&lt;li&gt;production-ready structure
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use it as a starting point instead of reinventing file upload for every project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related articles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/spring-boot-file-upload-api/" rel="noopener noreferrer"&gt;Spring Boot File Upload API (Clean Structure Guide)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Build a Spring Boot file upload API with clean structure. Learn MultipartFile handling, validation, and scalable storage design.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/" rel="noopener noreferrer"&gt;How to Deploy a Production File Server on a VPS for Free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn how to deploy a scalable file storage backend using Spring Boot. Step by step VPS setup with zero cost.&lt;/p&gt;




</description>
      <category>java</category>
      <category>springboot</category>
      <category>guide</category>
      <category>storage</category>
    </item>
    <item>
      <title>Stop Overcomplicating File Upload in Spring Boot</title>
      <dc:creator>buildbasekit</dc:creator>
      <pubDate>Tue, 31 Mar 2026 08:28:08 +0000</pubDate>
      <link>https://dev.to/buildbasekit/stop-overcomplicating-file-upload-in-spring-boot-g8c</link>
      <guid>https://dev.to/buildbasekit/stop-overcomplicating-file-upload-in-spring-boot-g8c</guid>
      <description>&lt;p&gt;Building a file upload API in Spring Boot looks simple… until it isn’t.&lt;/p&gt;

&lt;p&gt;You start with one endpoint, and suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;file paths are scattered everywhere&lt;/li&gt;
&lt;li&gt;validation is inconsistent&lt;/li&gt;
&lt;li&gt;storage logic leaks into controllers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve seen this turn into a mess very quickly.&lt;/p&gt;

&lt;p&gt;In some cases, teams end up rewriting their entire file handling logic.&lt;/p&gt;

&lt;p&gt;Here’s how to build it clean from the start.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why file upload APIs get messy
&lt;/h2&gt;

&lt;p&gt;File upload starts simple, but complexity grows fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;handling different file types&lt;/li&gt;
&lt;li&gt;managing storage paths&lt;/li&gt;
&lt;li&gt;adding validation&lt;/li&gt;
&lt;li&gt;supporting downloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without structure, this logic spreads across your codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core components of a file upload API
&lt;/h2&gt;

&lt;p&gt;Even a simple setup should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;upload endpoint to receive files&lt;/li&gt;
&lt;li&gt;download endpoint to retrieve files&lt;/li&gt;
&lt;li&gt;storage layer (local or cloud)&lt;/li&gt;
&lt;li&gt;validation for file size and type&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Spring Boot File Upload Works (Step by Step)
&lt;/h2&gt;

&lt;p&gt;Understanding the flow helps you design the API correctly from the start.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client sends file using multipart request&lt;/li&gt;
&lt;li&gt;Controller receives the file&lt;/li&gt;
&lt;li&gt;Service processes and validates it&lt;/li&gt;
&lt;li&gt;Storage layer saves the file&lt;/li&gt;
&lt;li&gt;API returns file reference or URL&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Best Practice: Structure Your Spring Boot File Upload API
&lt;/h2&gt;

&lt;p&gt;One of the biggest mistakes is mixing file handling logic directly into controllers. This makes it harder to change storage strategies later.&lt;/p&gt;

&lt;p&gt;A cleaner approach is to separate responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;controller handles request and response&lt;/li&gt;
&lt;li&gt;service handles file processing logic&lt;/li&gt;
&lt;li&gt;storage layer manages file saving and retrieval&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Recommended project structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
src/
 ├── controller/
 ├── service/
 ├── storage/
 ├── model/
 └── config/

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common mistakes to avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;storing files without proper naming strategy&lt;/li&gt;
&lt;li&gt;not validating file size or type&lt;/li&gt;
&lt;li&gt;hardcoding file paths&lt;/li&gt;
&lt;li&gt;no separation between upload and storage logic&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A better approach
&lt;/h2&gt;

&lt;p&gt;Start simple, but keep structure clear from day one.&lt;/p&gt;

&lt;p&gt;This makes it easy to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;switch from local to cloud storage&lt;/li&gt;
&lt;li&gt;add authentication later&lt;/li&gt;
&lt;li&gt;scale without rewriting everything&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Without vs with proper structure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Without structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;file logic inside controllers&lt;/li&gt;
&lt;li&gt;hardcoded paths&lt;/li&gt;
&lt;li&gt;difficult to switch storage&lt;/li&gt;
&lt;li&gt;code duplication&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  With structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;clean separation of layers&lt;/li&gt;
&lt;li&gt;easy to extend and maintain&lt;/li&gt;
&lt;li&gt;storage can be swapped&lt;/li&gt;
&lt;li&gt;reusable across projects&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Conclusion: Build a Clean File Upload API in Spring Boot
&lt;/h2&gt;

&lt;p&gt;File upload APIs do not need to be complicated. With a simple structure and clear separation of concerns, you can build something that is both easy to maintain and easy to extend.&lt;/p&gt;




&lt;h2&gt;
  
  
  Want a clean file upload setup without rebuilding it every time?
&lt;/h2&gt;

&lt;p&gt;I built a minimal Spring Boot boilerplate with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;clean controller, service, and storage separation
&lt;/li&gt;
&lt;li&gt;file upload and download endpoints
&lt;/li&gt;
&lt;li&gt;production-ready structure you can extend
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://buildbasekit.com/boilerplates/filora-fs-lite/" rel="noopener noreferrer"&gt;https://buildbasekit.com/boilerplates/filora-fs-lite/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use it as a starting point instead of reinventing file upload for every project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Related articles
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/file-upload-mistakes-spring-boot/" rel="noopener noreferrer"&gt;Spring Boot File Upload Mistakes (Common Issues)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Avoid common file upload mistakes in Spring Boot. Learn validation, storage design, and how to structure clean file upload APIs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buildbasekit.com/blogs/how-to-deploy-a-production-ready-file-server-on-a-vps-for-free/" rel="noopener noreferrer"&gt;How to Deploy a Production File Server on a VPS for Free&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Learn how to deploy a scalable file storage backend using Spring Boot. Step by step VPS setup with zero cost.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>guide</category>
      <category>storgae</category>
    </item>
  </channel>
</rss>
