After 20 years of programming, this is what comes to my mind when I hear "How do Design Programs":

1: Start with the data structure

The tools to operate on the data can be rewritten or improved without much pain. Changing the data structure is what hurts. If the data structure is elegant, the code will fall into place. If the data structure is messy, the code will grow into a giant hairy ball of madness.

2: Don't use a DB. Use human-readable text files

It makes everything so much easier. You can see if your data structure is elegant. Because then the files will be easy to read and navigate. You will be able to start up your software 20 years from now without any installation and without having to go through the hell that is setting up the environment and updating dependencies. The website you are reading this on, Hacker News, stores all data in text files.

3: Build small tools to operate on the data

Like Linus started git by writing small tools "git-add", "git-commit", "git-log" etc. On the web you can have one url to do one thing (/urser, /item, /edit here on HN) and map them to one code file on your disk. If you want to use a framework like Django, Laravel or Express, those will unfortunately make it a bit harder to do that. But you can still stick somewhat closely to this approach.

4: Let people use it right away

If you are building it for yourself, you already have a user. Great. If not, let your target group use it right away. Otherwise, you will waste many years of your life building software features nobody understands or even wants.

Regarding 2: I've had problems with this strategy. Even though I used the swap technique instead of overwriting, files easily get corrupted on common file systems, and if it's just the user force quitting the application during the operation. (I mostly use Ext4 for testing.) Whatever Sqlite does under the hood is way more resilient to such problems. I guess that's because the Sqlite developers have been testing ACID compliance extensively.

In one of my Racket applications with high integrity demands I've resorted to writing the temp file, swapping it with the original, then opening it read-only and verifying all of its contents. That is very reliable but obviously slow. But in another application written in Go I've switched entirely to Sqlite even for simple settings files and had no problems since.

At least for my needs, NeDB[0] is the best of both worlds for prototyping and early-stage production releases. It's human-readable, on-disk, greppable, still supports indexing and a subset of Mongo features while remaining serverless and in-memory.

[0] https://github.com/louischatriot/nedb