DEV Community

Cover image for How I Build Software From Scratch: From a Rough Idea to Architecture and Code

How I Build Software From Scratch: From a Rough Idea to Architecture and Code

When I build something from scratch, I almost never start by opening the IDE and writing code.

I start by drawing, using Excalidraw.

Not because I expect the first design to be perfect, but because I want to understand what I am actually building before deciding how to build it.

Recently, I was working on a secured inventory system involving controlled access, face recognition, physical lockers, and sensor hardware. This is roughly the process I followed.

1. I start with the requirements, but I don't try to predict everything

The first thing I write down is what the system needs to do.

In this case, things like requesting an item, registering a face, unlocking a locker only for an authorized person, deciding which user can access which inventory, and searching inventory by different attributes.

Then I think about the non-functional side: availability, failure tolerance, data volume, consistency requirements, reads vs. writes, security, and where the system could realistically fail.

From there, I sketch the obvious entities and a few APIs.

At this stage, I deliberately don't try to design the entire system.

Software changes. Requirements change. Things you thought were important turn out not to be important, and things nobody considered suddenly become critical.

The goal of the first diagram is clarity, not perfection.

Functional and non-functional requirements sketch
Writing the functional and non-functional requirements

2. Then I draw how the system interacts with the outside world

This becomes especially useful when the software isn't operating by itself.

In this project, I wasn't only talking to a database and a browser. I also had to integrate with a detection sensor board.

So before going deeper into the application architecture, I tried to understand the contract between the physical system and the software.

What does the sensor expect from me? What do I expect from it? What happens when an item is removed? What happens when communication fails? Which component owns the state? How does the browser know something changed physically?

Once those questions are visible on paper, the system becomes much easier to reason about.

I usually draw the ugly version first. Arrows everywhere. Notes beside components. Questions that still need answers.

That's fine.

Rough sketch of the main flows
Rough sketch of the main flows

3. Then I give the data model a little more structure

Once I understand the overall flow, I move into the schema and relationships.

At this point, I already know the main pieces of the system: users, face enrollments, requests, lockers, slots, inventory items, event logs. So I start connecting them and thinking about how the data should actually move between them, using draw.io.

I ask questions like:

  • Should the request point to a specific item, or just an item category?
  • Where should the current slot assignment live?
  • How do I keep track of who approved a request?
  • How do I record when something was collected, returned, moved, or retired?
  • What information should be preserved for auditing later?

The goal here still isn't to create a perfect database design.

It's to get enough clarity that when I start coding, I'm not discovering the core relationships for the first time.

And if I spot a major design issue here, changing the diagram is cheap. Changing it after the same assumption has spread through the API, services, database queries and frontend is much more expensive.

Schema design diagram
Schema design

4. Only after the problem is clear do I think seriously about the stack and patterns

If I'm already comfortable with the technology, I can move fairly quickly.

If I'm using something unfamiliar, I slow down.

For this project, I wanted to use Go. I didn't have years of production Go experience, so before trying to architect everything, I went through tutorials, skimmed books and articles, studied existing Go code, and learned enough to understand what good Go code should look like.

Not enough to memorize the language.

Enough to read the code, question it, and understand why it was structured that way.

The architecture eventually became a simple layered design:

Overview flow of the application
Overview flow of the application

There is also a small Observer / Publish-Subscribe pattern in the system.

When the sensor detects a physical change, the backend publishes an event. Connected browser sessions receive that event through a stream and can refresh their state.

Nothing crazy, and maybe not the best way to do it.

And that's kind of the point.

I don't want the most impressive architecture.

I want the architecture that makes the system easiest to understand, change and operate.

5. Then AI becomes my pair programmer, not my replacement

Once I understand the problem, the architecture and the patterns I want, I use AI heavily while implementing.

My workflow is closer to pair programming than "build this entire application for me."

Depending on the stack, I may use different coding assistants. I've had engineers recommend Claude for Go, while I often enjoy working with ChatGPT around Node.js and TypeScript.

I don't treat those as hard rules; I use whatever helps me reason through the code best.

I give it the logical problem and let it help with implementation, repetitive work and boilerplate.

Then I read the code.

I question things I don't understand.

Sometimes I rewrite it.

Sometimes I ask why a pattern was chosen.

Sometimes we go back and forth several times because the first solution doesn't fit the system.

It is definitely slower than blindly accepting generated code.

But I learn much more this way.

And more importantly, when something breaks six months later, I actually understand the software I'm responsible for.

That's basically my process

Requirements → system interactions → data model → architecture → learn what I don't know → implementation → iterate.

The diagrams aren't documentation created after the project.

They're part of how I think.

And none of them are sacred. If implementation teaches me that the design was wrong, I go back and change the design.

One thing I haven't covered much here is testing and validation. Checking inputs and outputs is very important, and it deserves its own post.

Building from scratch, at least for me, isn't about knowing everything before writing the first line of code.

It's about reducing uncertainty one layer at a time until the code becomes the obvious next step.


Originally published on LinkedIn.

Top comments (0)