<?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: Jonathan Uwineza</title>
    <description>The latest articles on DEV Community by Jonathan Uwineza (@jonathan_uwineza_2fce3f51).</description>
    <link>https://dev.to/jonathan_uwineza_2fce3f51</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4115541%2Fa47f8e09-9234-4c12-8161-5775eec3cc3a.png</url>
      <title>DEV Community: Jonathan Uwineza</title>
      <link>https://dev.to/jonathan_uwineza_2fce3f51</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jonathan_uwineza_2fce3f51"/>
    <language>en</language>
    <item>
      <title>From Idea to Production: How I Overcame the Challenges of Building Car-Immo</title>
      <dc:creator>Jonathan Uwineza</dc:creator>
      <pubDate>Fri, 09 Oct 2026 17:24:41 +0000</pubDate>
      <link>https://dev.to/jonathan_uwineza_2fce3f51/from-idea-to-production-how-i-overcame-the-challenges-of-building-car-immo-2kop</link>
      <guid>https://dev.to/jonathan_uwineza_2fce3f51/from-idea-to-production-how-i-overcame-the-challenges-of-building-car-immo-2kop</guid>
      <description>&lt;h2&gt;
  
  
  Building Car-Immo: The Real Challenges I’m Facing and How I’m Solving Them
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Suggested DEV.to tags: &lt;code&gt;webdev&lt;/code&gt;, &lt;code&gt;node&lt;/code&gt;, &lt;code&gt;nextjs&lt;/code&gt;, &lt;code&gt;programming&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Building an application by following a tutorial and developing a product for real users are two very different experiences.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;Car-Immo&lt;/strong&gt;, my goal is to create a platform that brings two markets together in one place: real estate and vehicles. Users should be able to search for a house, an apartment, a plot of land, or a vehicle. They should also be able to publish a listing and connect with interested buyers or renters.&lt;/p&gt;

&lt;p&gt;On paper, the idea sounds simple. In practice, it has made me think deeply about architecture, user roles, database design, deployment, security, and user experience.&lt;/p&gt;

&lt;p&gt;In this article, I share the main lessons I have learned while developing Car-Immo. This is not a tutorial presenting a perfect application. It is an honest look at the process of turning an idea into a full-stack product.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start With the Need, Not the Technology
&lt;/h2&gt;

&lt;p&gt;At the beginning of a project, it is tempting to immediately choose a framework, a database, and a long list of libraries. However, the first question should be: &lt;strong&gt;What problem does the application need to solve?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Among other things, Car-Immo needs to let users:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;browse real estate and vehicle listings;&lt;/li&gt;
&lt;li&gt;search and filter listings;&lt;/li&gt;
&lt;li&gt;create an account and sign in;&lt;/li&gt;
&lt;li&gt;publish and manage their own listings;&lt;/li&gt;
&lt;li&gt;connect advertisers with potential customers;&lt;/li&gt;
&lt;li&gt;administer and moderate the platform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This list helped me separate essential features from ideas that could wait for a future version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson one:&lt;/strong&gt; A useful product is not measured by the number of technologies it uses. A first version should focus on solving a small number of important problems well.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Choose an Architecture You Can Maintain
&lt;/h2&gt;

&lt;p&gt;For Car-Immo, I separated the frontend from the backend.&lt;/p&gt;

&lt;p&gt;The frontend uses &lt;strong&gt;Next.js and TypeScript&lt;/strong&gt;. It manages the pages, navigation, forms, and user interface. The backend is built with &lt;strong&gt;Node.js, Express, and TypeScript&lt;/strong&gt;. It exposes a REST API responsible for authentication, users, listings, visits, statistics, and administrative operations.&lt;/p&gt;

&lt;p&gt;The data is stored in &lt;strong&gt;PostgreSQL&lt;/strong&gt;, with &lt;strong&gt;Prisma ORM&lt;/strong&gt; used to define the schema and communicate with the database. The backend is also prepared to use Redis when caching or distributed rate limiting becomes necessary.&lt;/p&gt;

&lt;p&gt;A simplified view of the architecture 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;Browser
    ↓
Next.js frontend
    ↓ HTTPS requests
Express API
    ↓
Prisma ORM
    ↓
PostgreSQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation provides flexibility, but it also introduces several questions: Where should each part be deployed? How should CORS be handled? How can communication be secured? How can types be shared without creating too much coupling?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson two:&lt;/strong&gt; Every architectural decision provides benefits, but it also creates new responsibilities. Choose a solution only after understanding its operational consequences.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Design the Data Around the Business Domain
&lt;/h2&gt;

&lt;p&gt;A vehicle listing and a real estate listing share some information: a title, description, price, images, location, owner, and status.&lt;/p&gt;

&lt;p&gt;However, they also have different attributes. A vehicle can have a make, model, year, and mileage. A property can have a number of bedrooms, an area, or a transaction type.&lt;/p&gt;

&lt;p&gt;This creates a data-modeling decision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one large table containing many optional fields;&lt;/li&gt;
&lt;li&gt;two completely separate models;&lt;/li&gt;
&lt;li&gt;one shared model supplemented with category-specific information.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The right choice depends on the expected searches, filters, and future changes. This is not merely a database concern: the data model also shapes the API routes, forms, validation rules, and frontend display.&lt;/p&gt;

&lt;p&gt;I also learned that migrations must be treated as part of the product. Changing a schema locally is easy. Doing it in production without losing data requires a clear strategy, versioned migrations, and reliable backups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson three:&lt;/strong&gt; A database schema should represent the business domain, not merely the current shape of the user interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Authentication Is Not Enough: Authorization Matters Too
&lt;/h2&gt;

&lt;p&gt;Knowing that a user is signed in does not tell us what that user is allowed to do.&lt;/p&gt;

&lt;p&gt;A platform such as Car-Immo needs several rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;users can edit their own listings, but not someone else's;&lt;/li&gt;
&lt;li&gt;administrators can moderate listings and manage users;&lt;/li&gt;
&lt;li&gt;some features may depend on a subscription or account status;&lt;/li&gt;
&lt;li&gt;sensitive routes must be protected on the server, even if their buttons are hidden in the interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An interface that hides a button is not a security mechanism. Every important permission must be verified by the API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson four:&lt;/strong&gt; Authentication answers, “Who are you?” Authorization answers, “Are you allowed to perform this action?” A real application needs both.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. An Application That Works Locally Is Not Yet Production-Ready
&lt;/h2&gt;

&lt;p&gt;In my local environment, the frontend and backend could communicate over HTTP. After deploying the frontend to Vercel, the browser began blocking requests to the API.&lt;/p&gt;

&lt;p&gt;The frontend was loaded through an HTTPS address, while the backend was still accessible through HTTP. The browser therefore reported a &lt;strong&gt;Mixed Content&lt;/strong&gt; error.&lt;/p&gt;

&lt;p&gt;This problem taught me that deployment is not simply the final command in a project. You also need to prepare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;environment variables;&lt;/li&gt;
&lt;li&gt;HTTPS for all public communication;&lt;/li&gt;
&lt;li&gt;the allowed CORS origins;&lt;/li&gt;
&lt;li&gt;database migrations;&lt;/li&gt;
&lt;li&gt;logs and error messages;&lt;/li&gt;
&lt;li&gt;a health endpoint;&lt;/li&gt;
&lt;li&gt;a backup and rollback procedure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A variable such as the API URL must also be configured for the correct environment. A value used locally should not be copied automatically into production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson five:&lt;/strong&gt; Production should be considered during development, not only after the application is finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. A Good Health Check Makes Troubleshooting Much Easier
&lt;/h2&gt;

&lt;p&gt;I added an &lt;code&gt;/api/health&lt;/code&gt; route to check the overall state of the backend. It can indicate whether the server is responding, whether PostgreSQL is reachable, and whether optional services such as Redis are enabled.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"connected"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"redis"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This route does not replace a proper monitoring solution. However, it quickly answers an essential question: Is the service running, and can it reach its main dependencies?&lt;/p&gt;

&lt;p&gt;It is also important to avoid exposing secrets, connection details, or complete error traces through this endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson six:&lt;/strong&gt; Observability is not reserved for large companies. Even a small project needs useful logs and a simple way to check its status.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Docker Provides Consistency, but It Does Not Remove Deployment Decisions
&lt;/h2&gt;

&lt;p&gt;I used a multi-stage Docker image to build the API. One stage installs the dependencies and compiles the TypeScript code; another retains only the files required at runtime.&lt;/p&gt;

&lt;p&gt;This approach makes it possible to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a more reproducible environment;&lt;/li&gt;
&lt;li&gt;reduce the final image size;&lt;/li&gt;
&lt;li&gt;separate development dependencies from production dependencies;&lt;/li&gt;
&lt;li&gt;run the same application on different infrastructures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, Docker does not automatically configure HTTPS, PostgreSQL backups, firewall rules, or secrets. It packages the application; it does not replace the entire infrastructure.&lt;/p&gt;

&lt;p&gt;I also had to distinguish between three Prisma operations that are often confused: generating the client, applying migrations, and inserting initial data. These operations can be automated, but they should not be run blindly every time the application starts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson seven:&lt;/strong&gt; A container makes deployment more consistent, but a well-built Docker image alone cannot guarantee a reliable production environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Design Should Help the User Make a Decision
&lt;/h2&gt;

&lt;p&gt;For a marketplace, an attractive listing card is not enough. Users must quickly understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what is being offered;&lt;/li&gt;
&lt;li&gt;where the property or vehicle is located;&lt;/li&gt;
&lt;li&gt;how much it costs;&lt;/li&gt;
&lt;li&gt;what its important features are;&lt;/li&gt;
&lt;li&gt;what action to take next.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I therefore aimed for a modern, premium visual direction while maintaining a clear hierarchy. Filters, images, loading states, and the mobile experience matter just as much as colors and shadows.&lt;/p&gt;

&lt;p&gt;In a context where some users may have limited connectivity, image size and performance are not minor details. They are part of the user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson eight:&lt;/strong&gt; Design is not merely decorative. It should reduce the effort required to understand, compare, and take action.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Errors Are an Excellent Source of Learning
&lt;/h2&gt;

&lt;p&gt;During this project, I encountered errors involving Prisma, PostgreSQL, Docker, CORS, HTTPS, environment variables, and deployment. They slowed down the work at the time, but each one can become useful documentation.&lt;/p&gt;

&lt;p&gt;I now try to record the following for every problem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;the exact error message;&lt;/li&gt;
&lt;li&gt;the context in which it appears;&lt;/li&gt;
&lt;li&gt;the hypotheses I tested;&lt;/li&gt;
&lt;li&gt;the actual cause;&lt;/li&gt;
&lt;li&gt;the solution I chose;&lt;/li&gt;
&lt;li&gt;how to prevent it from happening again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This method turns debugging into reusable knowledge and also makes it easier to write technical articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Car-Immo is still evolving, but this project has already taught me that a full-stack product involves much more than connecting a user interface to a database.&lt;/p&gt;

&lt;p&gt;You need to understand the problem, model the data, protect sensitive actions, prepare for deployment, monitor the state of your services, and continuously improve the user experience.&lt;/p&gt;

&lt;p&gt;My main conclusion is simple: &lt;strong&gt;Real-world projects teach what tutorials rarely show—the interactions between every part of a system.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the next article in this series, I will explain how I diagnosed a Mixed Content error between a frontend deployed on Vercel and an Express API exposed over HTTP, and why changing the CORS configuration could not solve the problem.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>softwaredevelopment</category>
      <category>startup</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
