<?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: The Saint</title>
    <description>The latest articles on DEV Community by The Saint (@the_saint_dac63e343ee8704).</description>
    <link>https://dev.to/the_saint_dac63e343ee8704</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%2F4012334%2F54ab9b77-3cfe-44cb-ba65-f72e5ac65d1e.png</url>
      <title>DEV Community: The Saint</title>
      <link>https://dev.to/the_saint_dac63e343ee8704</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/the_saint_dac63e343ee8704"/>
    <language>en</language>
    <item>
      <title>I built Clipsift — 47 private, in-browser media tools with zero cloud uploads</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Sun, 08 Nov 2026 18:49:44 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/i-built-clipsift-47-private-in-browser-media-tools-with-zero-cloud-uploads-33jp</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/i-built-clipsift-47-private-in-browser-media-tools-with-zero-cloud-uploads-33jp</guid>
      <description>&lt;p&gt;Hey everyone! I got tired of file converter sites that cap file sizes, make you wait in queues, and upload personal photos/audio to unknown servers.&lt;/p&gt;

&lt;p&gt;So I built Clipsift&lt;br&gt;
: an open catalog of 47 audio, PDF, and image tools running 100% client-side in your browser using Canvas, WebAssembly, and modern web APIs. No server storage, no data collection.&lt;/p&gt;

&lt;p&gt;Just added vector SVG to high-res PNG conversion and PDF storyboard synthesis. Would love your feedback on performance and what tools you'd like added next!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>From one command to a live app on a $5 VPS</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Mon, 02 Nov 2026 11:24:36 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/from-one-command-to-a-live-app-on-a-5-vps-nn0</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/from-one-command-to-a-live-app-on-a-5-vps-nn0</guid>
      <description>&lt;p&gt;You own the server, the code and the data. Here is what the first ten minutes look like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the default path gets annoying
&lt;/h2&gt;

&lt;p&gt;You rent a backend and it works. The annoyance shows up later: on app number two, when you see what a second project costs, and on the day you want to leave and find out what you can take with you.&lt;/p&gt;

&lt;p&gt;The database is yours in theory. In practice the export is a job you write yourself, the users sit in someone else's auth format, and the files live in a bucket you do not control.&lt;/p&gt;

&lt;p&gt;That is a trade, not a scandal, and it is worth seeing what the other side of it looks like. This post walks through one: a backend you create on your own machine, deploy to a server you rent, and can leave with one command. I build CrescoDB, so that is the tool in the examples, and I will say where it stops.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the project
&lt;/h2&gt;

&lt;p&gt;You need Node 20 or newer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create crescodb@latest my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This writes the project into &lt;code&gt;my-app&lt;/code&gt;, installs the CLI inside it, starts it and opens the dashboard. The dashboard asks you to sign in to a CrescoDB account. The account is free and needs no card.&lt;/p&gt;

&lt;p&gt;Now add auth. Move into the folder and run the generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
npx cresco create auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command writes the auth code into your project as plain files: &lt;code&gt;register.js&lt;/code&gt;, &lt;code&gt;login.js&lt;/code&gt;, &lt;code&gt;middleware.js&lt;/code&gt; and &lt;code&gt;jwt.js&lt;/code&gt;, wired to the project database. Passwords are hashed with scrypt and sessions use JWT. Next to them sit &lt;code&gt;schema.cresco&lt;/code&gt;, which describes your tables, and &lt;code&gt;cresco.db&lt;/code&gt;, the SQLite file.&lt;/p&gt;

&lt;p&gt;It is a normal Node project. Open it in an editor and read the login route. Nothing in it is hidden behind a package you cannot open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API starts on port 3000 and the dashboard is at &lt;code&gt;http://localhost:3000/studio&lt;/code&gt;. The database is SQLite, so there is nothing to install and no Docker.&lt;/p&gt;

&lt;p&gt;For the store starter, the project's own page shows this output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ schema.cresco · cresco.db · store starter
✓ shop is ready.
REST API on :3000
Dashboard opened, connected to shop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your app is served too. &lt;code&gt;cresco dev&lt;/code&gt; serves your project folder at &lt;code&gt;http://localhost:3000/app&lt;/code&gt; and reloads the page when you save, so the frontend sits next to its API.&lt;/p&gt;

&lt;h2&gt;
  
  
  The schema
&lt;/h2&gt;

&lt;p&gt;Everything comes from one file. A model looks like this:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uuid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"primaryKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"access"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"read"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"authenticated"&lt;/span&gt;&lt;span class="p"&gt;]&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;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;From that, every model gets create, read, update and delete over REST, with paging and filtering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /orders
POST /orders
PATCH /orders/:id
DELETE /orders/:id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same schema drives the rest. Auth has four roles: guest, user, mod and admin. A model can declare a &lt;code&gt;rowOwner&lt;/code&gt; column so users only see and change their own rows. Files go through bucket storage at &lt;code&gt;/storage&lt;/code&gt;, and row changes stream to connected clients over server-sent events.&lt;/p&gt;

&lt;p&gt;Tables are private by default. A model with no &lt;code&gt;access&lt;/code&gt; block needs a signed-in user to read it. Public has to be something you type on purpose.&lt;/p&gt;

&lt;p&gt;The engine underneath is SQLite by default. Point the project at PostgreSQL or MySQL and &lt;code&gt;schema.cresco&lt;/code&gt; and the API stay the same, so there is no rewrite between SQLite on your laptop and Postgres on the server. One caution: the first switch off SQLite carries your schema over, but moving data between engines later is an export and an import.&lt;/p&gt;

&lt;p&gt;Before anything ships, run the audit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cresco audit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It reads the schema in your repo and answers one question: who can read each table. A public table holding an email column is critical. A public price list is a note. It exits non-zero on a critical finding, so it also works in CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy
&lt;/h2&gt;

&lt;p&gt;You need a server. Any Ubuntu or Debian VPS works. Hetzner, DigitalOcean and Vultr all rent a small one for about $5 a month.&lt;/p&gt;

&lt;p&gt;In the dashboard, go to Servers &amp;amp; Apps and connect a server. Give it a name like &lt;code&gt;web-1&lt;/code&gt;. The dashboard shows one command with your token and key filled in. Paste it into the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; .../connect.sh | &lt;span class="nb"&gt;sudo &lt;/span&gt;bash &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; ... &lt;span class="nt"&gt;--url&lt;/span&gt; ... &lt;span class="nt"&gt;--pubkey&lt;/span&gt; &lt;span class="s1"&gt;'...'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It installs Docker if the box has none, and creates a separate &lt;code&gt;cresco&lt;/code&gt; user with no sudo access. It never asks for your root password. The token works once and stops working when the server checks in.&lt;/p&gt;

&lt;p&gt;Then, from your project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cresco login
npx cresco deploy &lt;span class="nt"&gt;--server&lt;/span&gt; web-1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;cresco deploy&lt;/code&gt; runs the access audit first and refuses to continue on a critical finding. If it passes, the project is sent to the server, built there, started and health-checked. The app gets a free address under &lt;code&gt;crescoafrica.com&lt;/code&gt; with HTTPS. For your own domain, the dashboard shows the DNS record to add and checks it for you. If the record sits behind Cloudflare, turn the orange cloud off so it is set to "DNS only", or certificate issuing fails. HTTPS comes from Caddy and Let's Encrypt, and it renews on its own.&lt;/p&gt;

&lt;p&gt;Rollback is built in. The previous version stays on the server. If a new build fails, the old one keeps serving. If it builds but fails its health check, you are told why, and Roll back in the dashboard puts the previous build back.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you still own after deploying
&lt;/h2&gt;

&lt;p&gt;Run this in the project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cresco eject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It writes a folder that runs your project without CrescoDB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myapp-ejected/
  schema.cresco
  data.sql
  vendor/        the engine, packaged into the folder
  Dockerfile
  docker-compose.yml
  README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;data.sql&lt;/code&gt; holds every table and every row as plain SQL, and &lt;code&gt;psql&lt;/code&gt;, &lt;code&gt;mysql&lt;/code&gt; and &lt;code&gt;sqlite3&lt;/code&gt; all load it. To run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;myapp-ejected
docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two limits are worth stating. The bundle is not air-gapped: Docker still pulls a Node base image and npm still fetches ordinary packages, like any Docker build. And two things need changing before you put it anywhere real: the database password in &lt;code&gt;docker-compose.yml&lt;/code&gt; is a placeholder, and &lt;code&gt;CRESCO_AUTH_SECRET&lt;/code&gt; must be set if the project uses auth. If the data export fails, &lt;code&gt;eject&lt;/code&gt; writes nothing and tells you why, so you never end up holding a bundle with an empty &lt;code&gt;data.sql&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is not
&lt;/h2&gt;

&lt;p&gt;The server is yours, so you are the operator. You pick it, you pay for it, and its disk, its updates and its uptime are your responsibility. CrescoDB reads CPU, memory and disk from the box and alerts you, but it does not turn a rented VPS into a managed service.&lt;/p&gt;

&lt;p&gt;It also does not mean none of your data touches CrescoDB. Project state and backups sit on CrescoDB infrastructure. Your live app and its database run on your server. What leaves it is the backup: each automatic snapshot is encrypted on your server with AES-256 before upload, then stored in Cloudflare R2, with the key held encrypted on our side. A restore check loads the latest backup into a scratch database on your server and counts what came out before the copy is marked verified. Automatic backups and restore checks are part of the paid plans. The free plan has none, and you can run a restore by hand.&lt;/p&gt;

&lt;p&gt;If you stop using the service, the app keeps running and your database and code stay with you. You lose the backups and the alerting.&lt;/p&gt;

&lt;h2&gt;
  
  
  One thing you can do in the browser
&lt;/h2&gt;

&lt;p&gt;If you only want to check one thing before installing anything, run the free access audit on the CrescoDB site. It needs no account. It reads your table rules and tells you which tables anyone can read. I put the link in the first comment.&lt;/p&gt;

&lt;p&gt;If you try any of this, I would like to hear where it broke, because that is the part I cannot see from my own machine.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>node</category>
      <category>selfhosting</category>
      <category>devops</category>
    </item>
    <item>
      <title>Seven things I check before running Postgres on a server I own</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Thu, 29 Oct 2026 09:22:20 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/seven-things-i-check-before-running-postgres-on-a-server-i-own-1obo</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/seven-things-i-check-before-running-postgres-on-a-server-i-own-1obo</guid>
      <description>&lt;p&gt;Self-hosting Postgres is not hard. Skipping one of these seven is.&lt;/p&gt;

&lt;p&gt;I build CrescoDB, a backend you deploy to a server you already rent, so I spend a lot of time thinking about what goes wrong on self-hosted setups. The failures are almost never clever. They are the same seven things, skipped once.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Something that restarts it at boot
&lt;/h2&gt;

&lt;p&gt;Postgres will run for weeks and then not come back after a reboot, because the service was never enabled. Enable the database and the app, then reboot the box and watch them return.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;postgresql
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;your-app
&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have not rebooted, you do not know. A service that is running right now has told you nothing about whether it starts on its own.&lt;/p&gt;

&lt;p&gt;This is the habit I am trying to build into CrescoDB: a deploy is not finished when the app answers once, it is finished when the whole stack has shown it comes back after a restart.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. A connection pool, before you think you need one
&lt;/h2&gt;

&lt;p&gt;Postgres starts a process for every connection. Give each app instance its own pool and the total multiplies faster than you expect, usually just after a traffic bump or a second instance.&lt;/p&gt;

&lt;p&gt;PgBouncer in transaction mode, placed in front of the database, keeps the real connection count small. One caveat: transaction mode does not play well with session-level features such as advisory locks or some prepared statement setups, so check your driver before you switch.&lt;/p&gt;

&lt;p&gt;Watch connections, not CPU. Running out of connections looks like a mystery outage while the CPU graph stays calm.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. A restore, not a backup
&lt;/h2&gt;

&lt;p&gt;A dump file is a hypothesis. The only proof is restoring it into a scratch database and counting rows against the source, on a schedule.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;createdb scratch_check
pg_restore &lt;span class="nt"&gt;--no-owner&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; scratch_check /backups/latest.dump
psql &lt;span class="nt"&gt;-d&lt;/span&gt; scratch_check &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"SELECT count(*) FROM your_main_table;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that restore has never run, you do not have a backup. You have a file. I wrote about this in more detail in &lt;a href="https://dev.to/the_saint_dac63e343ee8704/a-backup-youve-never-restored-is-just-a-rumour-oil"&gt;A backup you've never restored is just a rumour&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Certificates that renew themselves, and a database that is not public
&lt;/h2&gt;

&lt;p&gt;Two separate checks that fail in the same quiet way.&lt;/p&gt;

&lt;p&gt;For certificates, use something that renews on its own, such as certbot with a timer or a proxy like Caddy that handles it, and check that the renewal actually ran, not just that it was set up.&lt;/p&gt;

&lt;p&gt;For the database, bind it to localhost or a private address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;listen_addresses&lt;/span&gt; = &lt;span class="s1"&gt;'localhost'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Port 5432 open to the internet gets found. It is not a question of if.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Migrations in one direction
&lt;/h2&gt;

&lt;p&gt;Forward only, applied as part of the deploy, recorded somewhere you can read. The day someone edits a table by hand in production, your migrations stop describing your database, and every later deploy is a guess.&lt;/p&gt;

&lt;p&gt;If a change turns out wrong, write a new migration that fixes it. Do not reach into the live schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Alerts on the boring numbers
&lt;/h2&gt;

&lt;p&gt;Disk, connections, failed backups, and whether the app answers. That is the short list.&lt;/p&gt;

&lt;p&gt;CPU graphs feel productive and tell you very little. Disk is the one that takes you down without warning, because it fills slowly and then everything stops at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Log rotation
&lt;/h2&gt;

&lt;p&gt;A chatty app fills the disk with logs, and then Postgres stops accepting writes. Set up logrotate, or cap the journal, before you need it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;journalctl &lt;span class="nt"&gt;--vacuum-size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;500M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a permanent cap, set &lt;code&gt;SystemMaxUse&lt;/code&gt; in the journald config.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;None of this is complicated. All of it is easy to skip on a Friday afternoon, and each one costs a weekend you do not get back.&lt;/p&gt;

&lt;p&gt;If you only do one thing today, restore your last backup into a scratch database and count the rows. That is the one everyone skips.&lt;/p&gt;

&lt;p&gt;If you want to see how I am approaching this for deploys to your own server, CrescoDB is at crescodb.com.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>selfhosting</category>
      <category>devops</category>
      <category>backend</category>
    </item>
    <item>
      <title>A backup you've never restored is just a rumour</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Tue, 27 Oct 2026 21:12:01 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/a-backup-youve-never-restored-is-just-a-rumour-oil</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/a-backup-youve-never-restored-is-just-a-rumour-oil</guid>
      <description>&lt;p&gt;I have 24 users and zero of them have deployed. If one of them deploys tomorrow and their server dies on Thursday, the only thing standing between them and a lost week is a backup I have never opened.&lt;/p&gt;

&lt;p&gt;That sentence bothered me enough to change how I think about backups. Here is where I landed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everyone has a backup job. Almost nobody has a restore.
&lt;/h2&gt;

&lt;p&gt;Ask a room of developers whether their database is backed up and most hands go up. Ask how many have restored one of those backups, start to finish, and the hands mostly come down.&lt;/p&gt;

&lt;p&gt;A backup job that exits with code 0 tells you a file was written. It does not tell you the file is complete, that it matches the schema you are running today, or that it will load on a fresh machine at 2am. Until you have restored it, you are trusting a file you have never opened. That is a rumour with a timestamp.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a real test looks like
&lt;/h2&gt;

&lt;p&gt;A real test is short to describe:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Restore the backup into a scratch database that nothing else uses.&lt;/li&gt;
&lt;li&gt;Query it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The second step is the one people skip. A restore that finishes without errors can still hand you a database with missing rows, an empty table, or a sequence that restarted at 1. A backup that restores quietly with missing data is worse than no backup, because you will believe it.&lt;/p&gt;

&lt;p&gt;So the check is not "did the restore command succeed". It is "do the numbers look like production". Row counts on your main tables, the newest timestamp in a table that gets written constantly, and one real query your app depends on. If those come back right, you have evidence. If you only checked the exit code, you have a hope.&lt;/p&gt;

&lt;h2&gt;
  
  
  The loop I run
&lt;/h2&gt;

&lt;p&gt;This is the loop CrescoDB uses for apps deployed to a user's own server:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A nightly dump is taken on the server.&lt;/li&gt;
&lt;li&gt;That dump is restored into a scratch database on the same server and checked.&lt;/li&gt;
&lt;li&gt;The dump is copied off the box to R2.&lt;/li&gt;
&lt;li&gt;It is pulled back down from R2 and restored again somewhere else.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To be clear about where the data lives: backups sit in R2 as well as on the user's server. Your data is on your own machine, and a copy of the backup also lives in object storage. I would rather say that plainly than let anyone assume the backup never leaves the box.&lt;/p&gt;

&lt;p&gt;The second restore is the point of step four. Restoring on the same server proves the dump is readable. Restoring a copy that came back from storage, on a different machine, proves the copy survived the trip and that you can recover when the original server is gone, which is the case that actually matters.&lt;/p&gt;

&lt;p&gt;Automatic backups and scheduled restore checks are part of the paid plans, not the free tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  What only shows up on restore
&lt;/h2&gt;

&lt;p&gt;Backups tend to fail in quiet, boring ways. These are the ones worth checking for, and what to do about each:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Missing roles or ownership.&lt;/strong&gt; A dump may reference database roles that do not exist on the new machine, so objects fail to restore or end up owned by the wrong user. Create the roles first, or dump with ownership handled deliberately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extensions.&lt;/strong&gt; If your schema needs an extension and the new server does not have it installed, parts of the restore fail. Keep a list of required extensions next to the backup job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version mismatch.&lt;/strong&gt; Restoring a dump from a newer major version into an older server often does not work. Keep the restore target on the same major version, or newer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Truncated files.&lt;/strong&gt; A dump cut short by a full disk or a killed process can look like a normal file. Check the size against previous nights and let the restore fail loudly if the file is incomplete.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sequences and counters.&lt;/strong&gt; Row data can come back while sequences reset, which breaks the next insert. Test an insert after restoring, not just a select.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions on the backup itself.&lt;/strong&gt; A backup readable by the wrong users is its own problem. Check who can read the file, and treat it like production data, because it is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is exotic. It simply never gets discovered until someone restores, which is the whole argument for restoring on a schedule instead of during an outage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test your own tonight
&lt;/h2&gt;

&lt;p&gt;Three commands, using Postgres as the example. Swap in your own names and paths.&lt;/p&gt;

&lt;p&gt;Take a dump:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_dump &lt;span class="nt"&gt;-Fc&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /tmp/backup.dump your_database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restore it into a scratch database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;createdb scratch_check &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pg_restore &lt;span class="nt"&gt;--no-owner&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; scratch_check /tmp/backup.dump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare it against production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-d&lt;/span&gt; scratch_check &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"SELECT count(*) FROM your_main_table;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the same count against your live database. If the numbers match and a recent row is present, you now know something you did not know this morning. Drop the scratch database when you are done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dropdb scratch_check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put those four lines in a script, run it from cron, and make it complain when the counts disagree. That is most of the work.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;A backup is only a backup after it has been restored and queried. Do it into a scratch database, check real numbers, and do it again from a copy that came back from somewhere else. If you want to see how this fits into deploying a backend to your own server, CrescoDB is at crescodb.com, and the free audit page is at crescodb.com/audit.&lt;/p&gt;

&lt;p&gt;And if you have a backup job running right now, go restore last night's. I would like to hear what you find.&lt;/p&gt;

</description>
      <category>backups</category>
      <category>postgres</category>
      <category>devops</category>
      <category>selfhosting</category>
    </item>
    <item>
      <title>Beyond Deployment: What's Next for CrescoDB, Plus a Clearer Path to Getting Started</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Tue, 20 Oct 2026 11:04:20 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/beyond-deployment-whats-next-for-crescodb-plus-a-clearer-path-to-getting-started-21ck</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/beyond-deployment-whats-next-for-crescodb-plus-a-clearer-path-to-getting-started-21ck</guid>
      <description>&lt;p&gt;Beyond Deployment: What's Next for CrescoDB, Plus a Clearer Path to Getting Started&lt;br&gt;
Building a backend is only part of the work.&lt;br&gt;
You define your data, build your API, connect your application, test it, and eventually deploy it. Your application is live. Your users can access it. Everything appears to be working.&lt;br&gt;
Then something breaks.&lt;br&gt;
A route begins returning server errors. A query becomes unexpectedly slow. Your server starts running out of disk space. Your application stops responding, and you don't notice until someone reports it.&lt;br&gt;
The problem isn't necessarily that something broke. Software breaks. The problem is finding out what happened, identifying where it happened, and getting enough information to fix it.&lt;br&gt;
That's the next part of the developer experience we're working on at CrescoDB.&lt;br&gt;
Our upcoming update focuses on production monitoring and the operational visibility developers need after deployment. We're also working to make CrescoDB's own getting-started experience clearer, because we've made that journey more confusing than it should be.&lt;br&gt;
Here's what's changing, why it matters, and how to get a project running in the meantime.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The next stage: understanding what happens after deployment
We've been building CrescoDB around the backend workflow: creating your data models, generating an API, adding authentication, verifying access rules, and deploying to infrastructure you control.
But a backend doesn't stop being your responsibility when it goes live.
Once an application is in production, you need answers to questions like:&lt;/li&gt;
&lt;li&gt;Which endpoint is returning errors?&lt;/li&gt;
&lt;li&gt;Are requests getting slower?&lt;/li&gt;
&lt;li&gt;Which database queries are taking the longest?&lt;/li&gt;
&lt;li&gt;Is the server running low on memory or disk space?&lt;/li&gt;
&lt;li&gt;Is the application actually reachable by users?&lt;/li&gt;
&lt;li&gt;If something fails, will you know before a user has to tell you?
Without monitoring, answering those questions can involve manually checking logs, connecting to servers, running commands, and trying to reconstruct what happened after the fact.
We're working to make that process less painful.
Application monitoring
The monitoring capabilities we're preparing are designed to give developers visibility into their application's behavior, rather than simply reporting whether the application process is running.
That includes information about:&lt;/li&gt;
&lt;li&gt;Requests and errors grouped by route.&lt;/li&gt;
&lt;li&gt;Server and client error rates.&lt;/li&gt;
&lt;li&gt;Request latency, including p95 latency.&lt;/li&gt;
&lt;li&gt;Slow database queries and query failures.&lt;/li&gt;
&lt;li&gt;Application errors with useful debugging information.&lt;/li&gt;
&lt;li&gt;Runtime indicators such as memory usage and event-loop delay.
For example, imagine that a store's checkout endpoint starts returning server errors while the rest of the application continues working.
A generic alert saying "your application has an error" leaves you with more questions.
Knowing that POST /checkout is failing, seeing how many requests were affected, and having information to investigate the failure gives you a much more useful starting point.
The goal is not to replace debugging. It's to help you find the right place to begin.
Server monitoring
Application health and server health are related, but they're not the same thing.
An application can encounter problems because of its own code. It can also encounter problems because the machine running it is under pressure.
We're expanding the visibility developers have into the infrastructure supporting their applications, including:&lt;/li&gt;
&lt;li&gt;CPU usage and system load.&lt;/li&gt;
&lt;li&gt;Available memory and swap.&lt;/li&gt;
&lt;li&gt;Disk usage and partitions.&lt;/li&gt;
&lt;li&gt;Running containers and their resource usage.&lt;/li&gt;
&lt;li&gt;Server availability.&lt;/li&gt;
&lt;li&gt;Operational problems that may require attention.
Consider a server whose disk is nearly full.
That can interfere with deployments, backups, and database operations. Finding out only when a deployment fails is much later than finding out while there's still time to act.
The idea is to make those conditions visible before they turn into a larger incident.
Uptime checks from outside the server
Monitoring an application from inside its own process is useful, but it cannot tell you everything a visitor experiences.
A server might be running while its public domain is misconfigured. A certificate might have a problem. A CDN might be returning an error. A URL might be reachable but taking too long to respond.
That's why external uptime checks matter.
CrescoDB's monitoring direction also includes checking public URLs from outside the server, helping identify problems involving reachability, DNS, TLS, response times, and the page being served.
The objective is to see the application from more than one perspective: from inside the app, from the server underneath it, and from the outside where users encounter it.
Alerts that tell you when something needs attention
Monitoring is less useful if you have to keep a dashboard open all day to benefit from it.
Alerts are intended to bring important problems to your attention so you can investigate them.
CrescoDB's documented monitoring capabilities include email alerts, with Slack and Telegram available on paid plans. The intention is to help developers respond to meaningful failures without having to watch every metric manually.
We're working toward a better operational workflow: detect a problem, understand where it is happening, investigate it, and work toward a resolution.
The important distinction is that we're not trying to give developers more numbers to stare at. We're trying to make production problems easier to understand and act on.
You can read more about the direction in the current CrescoDB product overview and production monitoring documentation.&lt;/li&gt;
&lt;li&gt;Deployment is part of the workflow, not the end of it
We've also been thinking about the steps between a working local project and a working production application.
Developers have different workflows. Some prefer Git-based deployment. Others want to deploy an existing project without setting up a repository workflow first.
We're working on reducing friction in that process, including ZIP-based deployment and better ways to manage disk allocation when multiple applications share a server.
The goal is to make deployment more accessible without hiding the important decisions from developers.
CrescoDB is built around the idea that you should be able to run your application on infrastructure you control. Your code and data shouldn't become inaccessible simply because you decide to change platforms.
That means deployment should be understandable, operational problems should be easier to diagnose, and leaving the platform should remain a practical option.
We're not going to pretend that every part of this workflow is finished. We're continuing to improve it.&lt;/li&gt;
&lt;li&gt;We also owe you an apology
Before talking about more features, we need to acknowledge something about the current product experience.
Our homepage and onboarding instructions have been confusing, and we're sorry.
We've tried to communicate a lot about CrescoDB: local development, the database and API, authentication, the dashboard, AI assistance, deployment, and the operational tools around production.
Those capabilities matter. But presenting them without making the starting point obvious can leave a new developer wondering what to do first.
Some of the important distinctions haven't been clear enough:&lt;/li&gt;
&lt;li&gt;What you need to install.&lt;/li&gt;
&lt;li&gt;How to create and run a local project.&lt;/li&gt;
&lt;li&gt;Which features work locally.&lt;/li&gt;
&lt;li&gt;When you need an account.&lt;/li&gt;
&lt;li&gt;What changes when you want to deploy.&lt;/li&gt;
&lt;li&gt;How to connect your project to a server you control.
We should have made those steps easier to follow.
A developer who wants to try CrescoDB shouldn't have to study the entire product before getting a working API. They shouldn't have to confuse local development with cloud deployment, either.
That's a communication and onboarding problem, and it's our responsibility to address it.
We're working to make the homepage more focused and the getting-started journey more direct. In the meantime, here's a practical guide you can follow.&lt;/li&gt;
&lt;li&gt;How to get a CrescoDB project running
You do not need to deploy to a server to begin using CrescoDB.
For local development, the prerequisites are straightforward: install Node.js 20 or newer, then install CrescoDB.
Step 1: Install Node.js
CrescoDB requires Node.js 20 or newer. Node.js 22 LTS is the recommended starting point.
Check your installed version:
node --version&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you need to install Node.js, use the official installer or a version manager.&lt;br&gt;
Step 2: Install CrescoDB&lt;br&gt;
Install the CLI globally:&lt;br&gt;
npm install -g crescodb&lt;/p&gt;

&lt;p&gt;You can also use npx crescodb if you prefer not to install it globally.&lt;br&gt;
Step 3: Create a project&lt;br&gt;
Create a new folder for your project, move into it, and initialize CrescoDB:&lt;br&gt;
mkdir my-project&lt;br&gt;
cd my-project&lt;br&gt;
cresco init&lt;/p&gt;

&lt;p&gt;This creates the basic project files, including the schema, configuration, and local SQLite database.&lt;br&gt;
Important: A new project starts empty. That is intentional. You define your own tables and data models rather than being given a pre-populated application.&lt;br&gt;
If you would rather start with demo tables, initialize a starter project instead:&lt;br&gt;
cresco init --starter&lt;/p&gt;

&lt;p&gt;Use one initialization option or the other for a new project.&lt;br&gt;
Step 4: Start the development server&lt;br&gt;
From your project directory, run:&lt;br&gt;
cresco dev&lt;/p&gt;

&lt;p&gt;CrescoDB starts the local REST API and development dashboard.&lt;br&gt;
Step 5: Open the dashboard&lt;br&gt;
Visit:&lt;br&gt;
&lt;a href="http://localhost:3000/studio" rel="noopener noreferrer"&gt;http://localhost:3000/studio&lt;/a&gt;&lt;br&gt;
From the dashboard, you can browse and edit data, design your schema, run SQL, inspect your models, and explore the project as you build it.&lt;br&gt;
You now have a local project to work with.&lt;br&gt;
You do not need a rented VPS, a cloud account, or Docker simply to reach this stage.&lt;br&gt;
Step 6: Add your data models&lt;br&gt;
Your project starts empty unless you chose the starter option.&lt;br&gt;
You can define tables through the schema file, use the dashboard's schema editor, or describe the data you want to create through the supported AI workflow.&lt;br&gt;
For example, a simple blog might need models for posts and comments. A store might need products and orders.&lt;br&gt;
Once your schema is in place, CrescoDB can generate the corresponding API.&lt;br&gt;
You can then use those endpoints from your frontend or another application over HTTP.&lt;br&gt;
Step 7: Add authentication and verify access rules&lt;br&gt;
When your application needs authentication, you can generate the authentication code:&lt;br&gt;
cresco create auth&lt;/p&gt;

&lt;p&gt;CrescoDB writes editable code into your project rather than requiring you to treat authentication as an opaque black box.&lt;br&gt;
Before deployment, you can also run:&lt;br&gt;
cresco audit&lt;/p&gt;

&lt;p&gt;This checks access rules against your schema and identifies critical exposure issues. CrescoDB's deployment workflow also runs the audit before preparing a deployment, refusing to proceed when it finds a critical issue unless you explicitly override it.&lt;br&gt;
Step 8: Deploy when you're ready&lt;br&gt;
Local development and production deployment are separate stages.&lt;br&gt;
Once your application is working locally, you can move on to deployment. CrescoDB supports deploying to a server you control, with a workflow for connecting the server, deploying the project, and configuring a domain.&lt;br&gt;
You don't have to do this before you can start building.&lt;br&gt;
For the deployment steps, use the official guide to deploying to your own server rather than trying to infer the process from the homepage.&lt;br&gt;
The full local quickstart is available in the CrescoDB getting-started guide.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What we're trying to build with CrescoDB
We don't want CrescoDB to be just another way to generate an API.
We want the workflow to make sense from the first local project through production:&lt;/li&gt;
&lt;li&gt;Build: Define your data and get a working backend.&lt;/li&gt;
&lt;li&gt;Verify: Check access rules before deploying.&lt;/li&gt;
&lt;li&gt;Deploy: Run your application on infrastructure you control.&lt;/li&gt;
&lt;li&gt;Monitor: Understand application errors and server health.&lt;/li&gt;
&lt;li&gt;Operate: Manage the ongoing work of keeping an application healthy.&lt;/li&gt;
&lt;li&gt;Own: Retain control over your code and data.
These parts are connected.
A project that runs locally but is difficult to deploy has an incomplete workflow. An application that deploys successfully but provides no useful operational visibility creates a different problem. And a platform that makes it difficult to leave isn't giving developers full control.
We're working on these parts together because they affect the experience of building and maintaining real software.&lt;/li&gt;
&lt;li&gt;What's next?
Tomorrow, we're sharing the next CrescoDB update, with a focus on production monitoring and the visibility developers need after deployment.
We're also continuing to improve deployment workflows and address the confusion around getting started.
We'll keep sharing what we're building, explaining what has changed, and listening to the developers using the product.
We know that shipping features isn't the only measure of progress. The product needs to be understandable, the documentation needs to be useful, and the path from trying CrescoDB to building something real needs to be clear.
We've got work to do on that, and we're taking it seriously.
Thank you to everyone who has tested CrescoDB, shared feedback, reported problems, or followed the project while it's evolving.
We're building this in public, including the parts we need to get better at.
Stay tuned for tomorrow's update.
Explore CrescoDB: CrescoDB official website
Get started: CrescoDB quickstart&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Hardest Part of Building a Developer Tool Isn't Writing Code</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Sun, 11 Oct 2026 08:59:21 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/the-hardest-part-of-building-a-developer-tool-isnt-writing-code-11oi</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/the-hardest-part-of-building-a-developer-tool-isnt-writing-code-11oi</guid>
      <description>&lt;p&gt;Building software is addictive.&lt;br&gt;
You think of a feature, open your editor, write some code, and eventually watch the idea become something real.&lt;br&gt;
That feeling is difficult to beat.&lt;br&gt;
But when you're building a product rather than working on an isolated project, there's a problem: you can spend months building things without getting any closer to building a product people actually need.&lt;br&gt;
I've been thinking about this while working on developer tools.&lt;br&gt;
The challenge isn't always figuring out how to implement something. Sometimes, it's deciding whether it should exist in the first place.&lt;br&gt;
The feature trap&lt;br&gt;
Imagine you're building a tool for developers.&lt;br&gt;
You start with a clear problem to solve. Then you notice another problem you could address.&lt;br&gt;
You add a feature.&lt;br&gt;
While implementing it, you discover something else that would make the experience better. You add that, too.&lt;br&gt;
Before long, your roadmap contains authentication, analytics, integrations, monitoring, deployment, automation, and several other ideas.&lt;br&gt;
Individually, each feature makes sense.&lt;br&gt;
Together, they can make the product harder to understand, harder to maintain, and harder to launch.&lt;br&gt;
The dangerous part is that you're still making progress.&lt;br&gt;
Your codebase is growing. Your feature list is getting longer. Your product looks more impressive.&lt;br&gt;
But none of those things necessarily means you've found product-market fit.&lt;br&gt;
You might simply be getting better at building things that people haven't asked for.&lt;br&gt;
More features don't automatically create more value&lt;br&gt;
A feature has at least two costs.&lt;br&gt;
The first is the cost of building it.&lt;br&gt;
The second is the cost of keeping it working.&lt;br&gt;
Every new capability can introduce additional edge cases, bugs, documentation requirements, support requests, and maintenance work.&lt;br&gt;
Integrations need to be maintained. APIs change. Dependencies become outdated. User expectations grow.&lt;br&gt;
A feature that takes two days to implement might create months of maintenance.&lt;br&gt;
This doesn't mean we should avoid ambitious products. It means we should understand the difference between the cost of creating a feature and the cost of owning it.&lt;br&gt;
For a small team, that distinction matters enormously.&lt;br&gt;
Start with the problem, not the feature&lt;br&gt;
When evaluating a new feature, I think these questions are more useful than asking whether it would be cool to build.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Who specifically has this problem?
If the answer is everyone, the answer probably isn't specific enough.
Identify the people who experience the problem and understand how they currently deal with it.&lt;/li&gt;
&lt;li&gt;How painful is the problem?
A minor inconvenience might not justify a new product feature.
A problem that repeatedly wastes hours, causes failures, or forces people to pay for several disconnected tools might be worth investigating.&lt;/li&gt;
&lt;li&gt;What do people do today?
Look at existing workflows, competitors, spreadsheets, scripts, workarounds, and manual processes.
The way people solve a problem today can reveal more than a list of features they say they want.&lt;/li&gt;
&lt;li&gt;Can we test the idea before fully building it?
Sometimes a prototype, landing page, manual service, or small experiment can tell you whether the idea deserves more investment.
You don't always need to build the entire system to test the underlying assumption.&lt;/li&gt;
&lt;li&gt;What will we stop doing to make room for it?
This is the question I think early-stage builders should ask more often.
Time is limited.
If a new feature takes two weeks, those are two weeks you cannot spend improving onboarding, fixing an existing problem, talking to users, or finding customers.
A roadmap should reflect those trade-offs.
Distribution is part of building the product
There's another trap developers can fall into: believing that a better product will automatically attract users.
It won't.
People need to discover your product, understand what it does, see why it matters, and decide that trying it is worth their time.
That means distribution cannot always wait until development is finished.
You can have excellent engineering and still struggle to get users if nobody knows your product exists.
For an early-stage startup, speaking to potential users, publishing useful content, participating in developer communities, and testing acquisition channels are not distractions from building.
They are part of discovering what is worth building.
What I'd do differently with a new product
If I were starting another developer tool from scratch, I'd keep the initial plan deliberately narrow.
I'd identify one specific audience and one painful problem.
Then I'd build the smallest version that could solve that problem for a real user.
I'd put it in front of people early, observe what they actually do, and use that information to decide what comes next.
I'd keep a separate list of interesting ideas so I wouldn't confuse an idea worth remembering with a feature worth implementing immediately.
Most importantly, I'd measure progress by what users can accomplish, not simply by how much code I've written.
The goal isn't to build the smallest product possible forever.
It's to earn the right to build a bigger product by proving that the smaller one is useful.
Final thought
Building software rewards people who can execute.
Building a sustainable product also requires knowing when not to execute.
There will always be another feature to add, another integration to support, and another idea that seems too good to ignore.
But an early-stage team doesn't have unlimited time or resources.
Sometimes, the most valuable thing you can do for your product is leave a good idea unbuilt.
I'm curious about other builders' experiences:
What's one feature you were convinced you needed, but later realised you could have shipped without?
I'd like to hear the reasoning behind it.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>webdev</category>
      <category>startup</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Your Application Is Not Your Server: Why Production Monitoring Needs Both</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Fri, 09 Oct 2026 19:20:30 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/your-application-is-not-your-server-why-production-monitoring-needs-both-56m7</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/your-application-is-not-your-server-why-production-monitoring-needs-both-56m7</guid>
      <description>&lt;p&gt;Building an application and keeping it healthy in production are two different engineering problems.&lt;/p&gt;

&lt;p&gt;You can have an application that starts successfully, responds to requests, and looks perfectly fine during a quick manual test. Then, under real usage, requests become slow, errors appear, or the application stops responding entirely.&lt;/p&gt;

&lt;p&gt;When that happens, where do you look?&lt;/p&gt;

&lt;p&gt;The application? The server? The network? The disk?&lt;/p&gt;

&lt;p&gt;The answer depends on what failed. That's why production monitoring needs to look at both the application and the infrastructure underneath it.&lt;/p&gt;

&lt;p&gt;Application health is only half the picture&lt;/p&gt;

&lt;p&gt;Application monitoring focuses on what the software itself is doing.&lt;/p&gt;

&lt;p&gt;Depending on the monitoring setup, useful signals include:&lt;/p&gt;

&lt;p&gt;Runtime errors and crashes&lt;/p&gt;

&lt;p&gt;Application availability&lt;/p&gt;

&lt;p&gt;Downtime&lt;/p&gt;

&lt;p&gt;Failed requests and unexpected behavior&lt;/p&gt;

&lt;p&gt;These signals help answer questions such as:&lt;/p&gt;

&lt;p&gt;Is the application still running?&lt;/p&gt;

&lt;p&gt;Has the process crashed?&lt;/p&gt;

&lt;p&gt;Is the service available to users?&lt;/p&gt;

&lt;p&gt;Are runtime errors occurring?&lt;/p&gt;

&lt;p&gt;But an application can experience problems because of something outside its own code.&lt;/p&gt;

&lt;p&gt;For example, a database operation may start failing because the server is running out of disk space. Requests may become slow because CPU usage is consistently high. An otherwise healthy application may become unreachable because the underlying server is unavailable.&lt;/p&gt;

&lt;p&gt;Looking only at application-level signals can make those problems harder to investigate.&lt;/p&gt;

&lt;p&gt;The server has its own health indicators&lt;/p&gt;

&lt;p&gt;The infrastructure running an application produces useful signals of its own.&lt;/p&gt;

&lt;p&gt;CPU usage: Sustained high CPU utilization can indicate that a workload is overwhelming available compute capacity.&lt;/p&gt;

&lt;p&gt;Disk space: A nearly full disk can affect logs, temporary files, databases and other write operations.&lt;/p&gt;

&lt;p&gt;Network latency: Increasing latency can indicate a connectivity or performance problem somewhere along the relevant network path.&lt;/p&gt;

&lt;p&gt;Server availability: If the underlying server becomes unavailable, applications hosted on it can become unreachable regardless of whether their code was healthy before the incident.&lt;/p&gt;

&lt;p&gt;These metrics don't automatically identify the root cause of every incident. They do, however, give you more information to investigate the problem.&lt;/p&gt;

&lt;p&gt;The important point is that application health and server health are related, but they aren't the same thing.&lt;/p&gt;

&lt;p&gt;Why small teams need both&lt;/p&gt;

&lt;p&gt;Large engineering organizations can build elaborate observability stacks with separate tools for metrics, logs, traces, alert routing and infrastructure monitoring.&lt;/p&gt;

&lt;p&gt;Small teams and independent developers often have a different problem: too many tools to configure and too many places to check when something breaks.&lt;/p&gt;

&lt;p&gt;The goal shouldn't be to collect every possible metric. It should be to collect enough useful signals to notice problems and begin investigating them.&lt;/p&gt;

&lt;p&gt;A practical starting point is:&lt;/p&gt;

&lt;p&gt;Know when the application becomes unavailable.&lt;/p&gt;

&lt;p&gt;Detect important runtime failures.&lt;/p&gt;

&lt;p&gt;Watch resource pressure on the underlying server.&lt;/p&gt;

&lt;p&gt;Alert the people responsible when something needs attention.&lt;/p&gt;

&lt;p&gt;Have a clear path to investigate the issue.&lt;/p&gt;

&lt;p&gt;Alert delivery matters, too. A dashboard is useful when you're looking at it. An alert is useful when you're doing something else.&lt;/p&gt;

&lt;p&gt;Email, Slack and Telegram provide different ways to route notifications into workflows developers already use.&lt;/p&gt;

&lt;p&gt;Bringing application and server monitoring together&lt;/p&gt;

&lt;p&gt;This is the problem we're addressing with Monitor, the sixth pillar of CrescoDB.&lt;/p&gt;

&lt;p&gt;CrescoDB is expanding its lifecycle tooling across Build, Own, Verify, Deploy, Operate and Monitor.&lt;/p&gt;

&lt;p&gt;With Monitor, we're bringing visibility into both the application and its underlying server. That includes application crashes, runtime errors and downtime, alongside server signals such as CPU usage, disk space, network latency and server availability.&lt;/p&gt;

&lt;p&gt;When something goes wrong, alerts can be sent through email, Slack and Telegram.&lt;/p&gt;

&lt;p&gt;The goal isn't to claim that one dashboard eliminates every production incident. It's to give developers a clearer view of what's happening across the layers they depend on.&lt;/p&gt;

&lt;p&gt;Monitoring belongs in the deployment workflow&lt;/p&gt;

&lt;p&gt;Deployment is not the end of the software lifecycle.&lt;/p&gt;

&lt;p&gt;After deployment, an application needs to keep running, respond to users, use resources responsibly and remain available as conditions change.&lt;/p&gt;

&lt;p&gt;That is why we think about the lifecycle as:&lt;/p&gt;

&lt;p&gt;Build → Own → Verify → Deploy → Operate → Monitor&lt;/p&gt;

&lt;p&gt;Each stage addresses a different concern, but they work together. Verification helps catch problems before release. Deployment gets the application running. Operations keep it running. Monitoring helps reveal when something needs attention.&lt;/p&gt;

&lt;p&gt;We're building CrescoDB around that connected workflow because developers shouldn't have to treat production reliability as an afterthought.&lt;/p&gt;

&lt;p&gt;A question for developers&lt;/p&gt;

&lt;p&gt;When one of your applications starts failing, how quickly can you tell whether the problem is in the application or the infrastructure running it?&lt;/p&gt;

&lt;p&gt;And what do you use to monitor both?&lt;/p&gt;

&lt;p&gt;We're building CrescoDB to make that workflow simpler. You can explore it at &lt;a href="https://crescodb.com" rel="noopener noreferrer"&gt;https://crescodb.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your App Being “Online” Doesn’t Mean It’s Healthy</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Tue, 06 Oct 2026 07:21:21 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/your-app-being-online-doesnt-mean-its-healthy-538c</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/your-app-being-online-doesnt-mean-its-healthy-538c</guid>
      <description>&lt;p&gt;One of the easiest traps in production is assuming:&lt;/p&gt;

&lt;p&gt;“The website is responding, so everything is fine.”&lt;/p&gt;

&lt;p&gt;It isn't.&lt;/p&gt;

&lt;p&gt;An application can technically be online while quietly falling apart underneath.&lt;/p&gt;

&lt;p&gt;Your API might still respond while CPU usage sits at 95%.&lt;/p&gt;

&lt;p&gt;Your server might still accept traffic while disk space has 300 MB left.&lt;/p&gt;

&lt;p&gt;Your database might be reachable while requests are taking five seconds instead of 200 milliseconds.&lt;/p&gt;

&lt;p&gt;Your process might still be running while one important background worker died twenty minutes ago.&lt;/p&gt;

&lt;p&gt;And sometimes the first person to tell you something is wrong is a user.&lt;/p&gt;

&lt;p&gt;That's usually too late.&lt;/p&gt;

&lt;p&gt;Uptime is only one signal&lt;/p&gt;

&lt;p&gt;The simplest form of monitoring asks one question:&lt;/p&gt;

&lt;p&gt;Is the application reachable?&lt;/p&gt;

&lt;p&gt;You send a request every few seconds or minutes.&lt;/p&gt;

&lt;p&gt;If it returns successfully:&lt;/p&gt;

&lt;p&gt;everything looks green.&lt;/p&gt;

&lt;p&gt;If it stops responding:&lt;/p&gt;

&lt;p&gt;send an alert.&lt;/p&gt;

&lt;p&gt;That is useful.&lt;/p&gt;

&lt;p&gt;But it only detects one class of failure.&lt;/p&gt;

&lt;p&gt;Production systems often degrade long before they completely disappear.&lt;/p&gt;

&lt;p&gt;Imagine this:&lt;/p&gt;

&lt;p&gt;Technically, the server is alive.&lt;/p&gt;

&lt;p&gt;Operationally, you should probably be worried.&lt;/p&gt;

&lt;p&gt;Monitoring the application&lt;/p&gt;

&lt;p&gt;Application monitoring looks at what is happening inside the software.&lt;/p&gt;

&lt;p&gt;Things like:&lt;/p&gt;

&lt;p&gt;application crashes&lt;br&gt;
unhandled exceptions&lt;br&gt;
failed requests&lt;br&gt;
response time&lt;br&gt;
API errors&lt;br&gt;
runtime failures&lt;br&gt;
unhealthy processes&lt;br&gt;
failed jobs&lt;/p&gt;

&lt;p&gt;Consider an API that normally responds in:&lt;/p&gt;

&lt;p&gt;and gradually moves to:&lt;/p&gt;

&lt;p&gt;Nothing has technically crashed.&lt;/p&gt;

&lt;p&gt;But something has changed.&lt;/p&gt;

&lt;p&gt;Maybe a database query became slower.&lt;/p&gt;

&lt;p&gt;Maybe traffic increased.&lt;/p&gt;

&lt;p&gt;Maybe a dependency is struggling.&lt;/p&gt;

&lt;p&gt;Maybe memory pressure is causing garbage collection problems.&lt;/p&gt;

&lt;p&gt;Monitoring gives you a chance to investigate before the application becomes unavailable.&lt;/p&gt;

&lt;p&gt;Then there is the machine underneath it&lt;/p&gt;

&lt;p&gt;This is the part I think developers sometimes forget.&lt;/p&gt;

&lt;p&gt;Your application isn't floating in the cloud.&lt;/p&gt;

&lt;p&gt;Eventually, it is running on a machine somewhere.&lt;/p&gt;

&lt;p&gt;That machine has:&lt;/p&gt;

&lt;p&gt;And any one of those can become the actual reason your application fails.&lt;/p&gt;

&lt;p&gt;For example, your Node.js API might be perfectly written.&lt;/p&gt;

&lt;p&gt;but It doesn't matter if the VPS disk is full.&lt;/p&gt;

&lt;p&gt;Your application can't write new files.&lt;/p&gt;

&lt;p&gt;Your database may not be able to expand.&lt;/p&gt;

&lt;p&gt;Logs stop being written.&lt;/p&gt;

&lt;p&gt;Eventually, things start breaking in strange ways.&lt;/p&gt;

&lt;p&gt;The application is the symptom.&lt;/p&gt;

&lt;p&gt;The server is the cause.&lt;/p&gt;

&lt;p&gt;CPU spikes matter&lt;/p&gt;

&lt;p&gt;Suppose your server normally runs around:&lt;/p&gt;

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

&lt;p&gt;for several minutes.&lt;/p&gt;

&lt;p&gt;That might mean:&lt;/p&gt;

&lt;p&gt;unexpected traffic&lt;br&gt;
runaway process&lt;br&gt;
expensive database operation&lt;br&gt;
infinite loop&lt;br&gt;
background job&lt;br&gt;
compromised server&lt;br&gt;
badly optimized code&lt;/p&gt;

&lt;p&gt;You don't necessarily want an alert every time CPU touches 90% for three seconds.&lt;/p&gt;

&lt;p&gt;That would become noise.&lt;/p&gt;

&lt;p&gt;But sustained abnormal usage?&lt;/p&gt;

&lt;p&gt;You probably want to know.&lt;/p&gt;

&lt;p&gt;Something like:&lt;/p&gt;

&lt;p&gt;CPU usage has remained above 90% for 5 minutes.&lt;/p&gt;

&lt;p&gt;That's actionable.&lt;/p&gt;

&lt;p&gt;Disk space is boring until it destroys your weekend&lt;/p&gt;

&lt;p&gt;Disk monitoring might be one of the least exciting things in software.&lt;/p&gt;

&lt;p&gt;Until this happens:&lt;/p&gt;

&lt;p&gt;Databases need disk.&lt;/p&gt;

&lt;p&gt;Logs need disk.&lt;/p&gt;

&lt;p&gt;Uploads need disk.&lt;/p&gt;

&lt;p&gt;Temporary files need disk.&lt;/p&gt;

&lt;p&gt;Docker images need disk.&lt;/p&gt;

&lt;p&gt;Backups may need disk.&lt;/p&gt;

&lt;p&gt;A slowly growing log directory can quietly consume storage for weeks before anything obviously fails.&lt;/p&gt;

&lt;p&gt;So instead of discovering:&lt;/p&gt;

&lt;p&gt;after the damage starts, monitoring should warn you earlier:&lt;/p&gt;

&lt;p&gt;The alert isn't the solution.&lt;/p&gt;

&lt;p&gt;It gives you time to create one.&lt;/p&gt;

&lt;p&gt;Network problems are different from downtime&lt;/p&gt;

&lt;p&gt;A server can be reachable and still have terrible connectivity.&lt;/p&gt;

&lt;p&gt;Maybe:&lt;/p&gt;

&lt;p&gt;Your users experience the application as slow.&lt;/p&gt;

&lt;p&gt;Your uptime monitor says:&lt;/p&gt;

&lt;p&gt;All systems operational.&lt;/p&gt;

&lt;p&gt;Both statements can technically be true.&lt;/p&gt;

&lt;p&gt;This is why latency belongs beside uptime.&lt;/p&gt;

&lt;p&gt;You want to understand:&lt;/p&gt;

&lt;p&gt;Is the server reachable?&lt;br&gt;
How quickly is it responding?&lt;br&gt;
Has network latency changed?&lt;br&gt;
Are packets being dropped?&lt;br&gt;
Is one region behaving differently?&lt;/p&gt;

&lt;p&gt;Availability and performance are related, but they are not the same thing.&lt;/p&gt;

&lt;p&gt;Monitoring without alerts is just a dashboard&lt;/p&gt;

&lt;p&gt;There is another mistake I've seen:&lt;/p&gt;

&lt;p&gt;Beautiful monitoring dashboards.&lt;/p&gt;

&lt;p&gt;Charts everywhere.&lt;/p&gt;

&lt;p&gt;CPU graphs.&lt;/p&gt;

&lt;p&gt;Memory graphs.&lt;/p&gt;

&lt;p&gt;Latency graphs.&lt;/p&gt;

&lt;p&gt;Error graphs.&lt;/p&gt;

&lt;p&gt;But nobody is staring at those dashboards at 3:17 AM.&lt;/p&gt;

&lt;p&gt;A monitoring system needs a way to say:&lt;/p&gt;

&lt;p&gt;Something changed. You should look at this.&lt;/p&gt;

&lt;p&gt;That could be:&lt;/p&gt;

&lt;p&gt;depending on how the team works.&lt;/p&gt;

&lt;p&gt;The important part is delivering the right alert to somebody capable of acting on it.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Production API Down&lt;/p&gt;

&lt;p&gt;api.example.com has failed 3 consecutive health checks.&lt;/p&gt;

&lt;p&gt;Last successful check: 02:41 UTC&lt;/p&gt;

&lt;p&gt;Or:&lt;/p&gt;

&lt;p&gt;Server CPU Alert&lt;/p&gt;

&lt;p&gt;CPU usage on production-01 has remained above 90% for 5 minutes.&lt;/p&gt;

&lt;p&gt;That is much more useful than discovering the problem because somebody posted:&lt;/p&gt;

&lt;p&gt;“Is your site down?”&lt;/p&gt;

&lt;p&gt;on Twitter.&lt;/p&gt;

&lt;p&gt;But alerts can also become useless&lt;/p&gt;

&lt;p&gt;The opposite problem is alert fatigue.&lt;/p&gt;

&lt;p&gt;If your system tells you:&lt;/p&gt;

&lt;p&gt;every thirty seconds, you eventually stop caring.&lt;/p&gt;

&lt;p&gt;Then the important notification gets buried with everything else.&lt;/p&gt;

&lt;p&gt;Good monitoring needs thresholds.&lt;/p&gt;

&lt;p&gt;And often, duration.&lt;/p&gt;

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

&lt;p&gt;CPU crossed 80%.&lt;/p&gt;

&lt;p&gt;think:&lt;/p&gt;

&lt;p&gt;CPU remained above 85% for five minutes.&lt;/p&gt;

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

&lt;p&gt;One request failed.&lt;/p&gt;

&lt;p&gt;think:&lt;/p&gt;

&lt;p&gt;Error rate exceeded 5% over the last five minutes.&lt;/p&gt;

&lt;p&gt;The goal isn't maximum notifications.&lt;/p&gt;

&lt;p&gt;The goal is useful awareness.&lt;/p&gt;

&lt;p&gt;Monitoring should exist at multiple layers&lt;/p&gt;

&lt;p&gt;I've started thinking about production monitoring as two connected systems.&lt;/p&gt;

&lt;p&gt;Layer 1 — Application&lt;/p&gt;

&lt;p&gt;Watch:&lt;/p&gt;

&lt;p&gt;Layer 2 — Infrastructure&lt;/p&gt;

&lt;p&gt;Watch:&lt;/p&gt;

&lt;p&gt;Because sometimes the application is broken.&lt;/p&gt;

&lt;p&gt;Sometimes the infrastructure is broken.&lt;/p&gt;

&lt;p&gt;And sometimes one is telling you something about the other.&lt;/p&gt;

&lt;p&gt;Seeing both makes diagnosis much easier.&lt;/p&gt;

&lt;p&gt;Monitoring shouldn't begin after something breaks&lt;/p&gt;

&lt;p&gt;This is probably the most important part.&lt;/p&gt;

&lt;p&gt;Teams often add monitoring after their first serious incident.&lt;/p&gt;

&lt;p&gt;Something goes down.&lt;/p&gt;

&lt;p&gt;Nobody notices for forty minutes.&lt;/p&gt;

&lt;p&gt;Users complain.&lt;/p&gt;

&lt;p&gt;The team fixes it.&lt;/p&gt;

&lt;p&gt;Then someone says:&lt;/p&gt;

&lt;p&gt;“We should probably add monitoring.”&lt;/p&gt;

&lt;p&gt;Monitoring shouldn't be the postmortem action item.&lt;/p&gt;

&lt;p&gt;It should be part of getting something into production.&lt;/p&gt;

&lt;p&gt;I've increasingly started thinking about the backend lifecycle as:&lt;/p&gt;

&lt;p&gt;Build → Verify → Deploy → Operate → Own → Monitor&lt;/p&gt;

&lt;p&gt;Building answers:&lt;/p&gt;

&lt;p&gt;Can we create it?&lt;/p&gt;

&lt;p&gt;Verification asks:&lt;/p&gt;

&lt;p&gt;Does it behave correctly?&lt;/p&gt;

&lt;p&gt;Deployment asks:&lt;/p&gt;

&lt;p&gt;Can we get it into production?&lt;/p&gt;

&lt;p&gt;Operations asks:&lt;/p&gt;

&lt;p&gt;Can we manage it there?&lt;/p&gt;

&lt;p&gt;Ownership asks:&lt;/p&gt;

&lt;p&gt;Do we retain control of what we're running?&lt;/p&gt;

&lt;p&gt;And monitoring asks:&lt;/p&gt;

&lt;p&gt;Will we know when something changes?&lt;/p&gt;

&lt;p&gt;That last question matters more once nobody is sitting there watching the terminal.&lt;/p&gt;

&lt;p&gt;This is something we're working on ourselves&lt;/p&gt;

&lt;p&gt;We've been thinking about this while building CrescoDB.&lt;/p&gt;

&lt;p&gt;CrescoDB already manages the production server layer for projects deployed through it, so we can see things like server availability and runtime health.&lt;/p&gt;

&lt;p&gt;We're now expanding that into a more complete monitoring layer.&lt;/p&gt;

&lt;p&gt;Not an attempt to recreate every observability feature from platforms built entirely around monitoring.&lt;/p&gt;

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

&lt;p&gt;Give smaller engineering teams the information they actually need to know when something is going wrong.&lt;/p&gt;

&lt;p&gt;At the application level:&lt;/p&gt;

&lt;p&gt;And underneath that:&lt;/p&gt;

&lt;p&gt;Then push useful alerts through the channels teams are already watching:&lt;/p&gt;

&lt;p&gt;Telegram, Slack and email.&lt;/p&gt;

&lt;p&gt;The interesting part for us isn't adding another dashboard.&lt;/p&gt;

&lt;p&gt;It's connecting application health with the infrastructure running it.&lt;/p&gt;

&lt;p&gt;Because if your API goes down at the same moment the server's memory hits 98%, those two pieces of information probably belong beside each other.&lt;/p&gt;

&lt;p&gt;Production is really about feedback&lt;/p&gt;

&lt;p&gt;When you're developing locally, feedback is immediate.&lt;/p&gt;

&lt;p&gt;Something breaks.&lt;/p&gt;

&lt;p&gt;Your terminal tells you.&lt;/p&gt;

&lt;p&gt;You fix it.&lt;/p&gt;

&lt;p&gt;Production creates distance between the developer and the software.&lt;/p&gt;

&lt;p&gt;Monitoring closes some of that distance.&lt;/p&gt;

&lt;p&gt;It tells you:&lt;/p&gt;

&lt;p&gt;Something changed.&lt;/p&gt;

&lt;p&gt;before the user has to.&lt;/p&gt;

&lt;p&gt;And that might be the simplest definition of good monitoring:&lt;/p&gt;

&lt;p&gt;Knowing your software is unhealthy before your customers do.&lt;/p&gt;

&lt;p&gt;What production issue have you had that proper monitoring would have caught earlier?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>devops</category>
      <category>backend</category>
    </item>
    <item>
      <title>AI Made Building Software Faster. It Didn’t Make Production Easier</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Fri, 02 Oct 2026 09:00:12 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/ai-made-building-software-faster-it-didnt-make-production-easier-1igi</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/ai-made-building-software-faster-it-didnt-make-production-easier-1igi</guid>
      <description>&lt;p&gt;A weird thing is happening in software development.&lt;/p&gt;

&lt;p&gt;Building software has become ridiculously fast.&lt;/p&gt;

&lt;p&gt;You can describe an application to an AI coding tool and have a working interface within minutes.&lt;/p&gt;

&lt;p&gt;Need an API?&lt;/p&gt;

&lt;p&gt;Generated.&lt;/p&gt;

&lt;p&gt;Database schema?&lt;/p&gt;

&lt;p&gt;Generated.&lt;/p&gt;

&lt;p&gt;Authentication flow?&lt;/p&gt;

&lt;p&gt;Probably generated too.&lt;/p&gt;

&lt;p&gt;A solo developer can now build something in a weekend that might have taken a small team several weeks a few years ago.&lt;/p&gt;

&lt;p&gt;That is genuinely impressive.&lt;/p&gt;

&lt;p&gt;But there’s a point where the speed suddenly disappears.&lt;/p&gt;

&lt;p&gt;It usually happens around the moment someone says:&lt;/p&gt;

&lt;p&gt;“Alright. Put it in production.”&lt;/p&gt;

&lt;p&gt;And now you have a completely different problem.&lt;/p&gt;

&lt;p&gt;Generating code and running software are different jobs&lt;/p&gt;

&lt;p&gt;AI is extremely good at helping us produce software.&lt;/p&gt;

&lt;p&gt;It can generate:&lt;/p&gt;

&lt;p&gt;routes&lt;br&gt;
database models&lt;br&gt;
API handlers&lt;br&gt;
authentication&lt;br&gt;
tests&lt;br&gt;
Dockerfiles&lt;br&gt;
CI configs&lt;/p&gt;

&lt;p&gt;But generating those things doesn't automatically mean the resulting system is ready to serve real users.&lt;/p&gt;

&lt;p&gt;Production introduces questions that don't matter nearly as much while you're working locally.&lt;/p&gt;

&lt;p&gt;What happens if your server crashes?&lt;/p&gt;

&lt;p&gt;What happens if a database migration fails halfway through?&lt;/p&gt;

&lt;p&gt;What happens when two users modify the same data simultaneously?&lt;/p&gt;

&lt;p&gt;Where are your logs?&lt;/p&gt;

&lt;p&gt;How do you know the application is healthy?&lt;/p&gt;

&lt;p&gt;Where are your backups?&lt;/p&gt;

&lt;p&gt;Have you ever restored one?&lt;/p&gt;

&lt;p&gt;How do you roll back a bad deployment?&lt;/p&gt;

&lt;p&gt;What happens when your server runs out of memory?&lt;/p&gt;

&lt;p&gt;Who gets alerted when any of this happens?&lt;/p&gt;

&lt;p&gt;AI can help answer every one of those questions.&lt;/p&gt;

&lt;p&gt;But somebody still has to ask them.&lt;/p&gt;

&lt;p&gt;The bottleneck is moving&lt;/p&gt;

&lt;p&gt;For years, one of the biggest constraints in software was simply writing the thing.&lt;/p&gt;

&lt;p&gt;You needed enough engineering hours to turn an idea into functioning software.&lt;/p&gt;

&lt;p&gt;AI is reducing that constraint.&lt;/p&gt;

&lt;p&gt;Not eliminating it, but reducing it significantly.&lt;/p&gt;

&lt;p&gt;So another constraint becomes more visible:&lt;/p&gt;

&lt;p&gt;Operating what we build.&lt;/p&gt;

&lt;p&gt;We are creating applications faster than ever.&lt;/p&gt;

&lt;p&gt;But those applications still need databases, deployments, infrastructure, monitoring, migrations, security, backups and recovery.&lt;/p&gt;

&lt;p&gt;If development becomes 5x faster while everything after development remains roughly the same, then production becomes a larger percentage of the work.&lt;/p&gt;

&lt;p&gt;That changes the developer workflow.&lt;/p&gt;

&lt;p&gt;“It works” is becoming an increasingly dangerous milestone&lt;/p&gt;

&lt;p&gt;There's a huge difference between:&lt;/p&gt;

&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;p&gt;It can survive production.&lt;/p&gt;

&lt;p&gt;An application running on your laptop usually has ideal conditions.&lt;/p&gt;

&lt;p&gt;You're probably the only user.&lt;/p&gt;

&lt;p&gt;The database is nearby.&lt;/p&gt;

&lt;p&gt;You know the environment.&lt;/p&gt;

&lt;p&gt;You can restart everything.&lt;/p&gt;

&lt;p&gt;You can read the terminal directly.&lt;/p&gt;

&lt;p&gt;If something breaks, you are literally sitting in front of it.&lt;/p&gt;

&lt;p&gt;Production removes most of those advantages.&lt;/p&gt;

&lt;p&gt;Now the application needs to behave correctly when nobody is watching.&lt;/p&gt;

&lt;p&gt;That means the definition of “finished” starts expanding.&lt;/p&gt;

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

&lt;p&gt;Feature works ✓&lt;/p&gt;

&lt;p&gt;you eventually need something closer to:&lt;/p&gt;

&lt;p&gt;Feature works ✓&lt;/p&gt;

&lt;p&gt;Tests pass ✓&lt;/p&gt;

&lt;p&gt;Configuration verified ✓&lt;/p&gt;

&lt;p&gt;Migration safe ✓&lt;/p&gt;

&lt;p&gt;Deployment reproducible ✓&lt;/p&gt;

&lt;p&gt;Monitoring active ✓&lt;/p&gt;

&lt;p&gt;Backups available ✓&lt;/p&gt;

&lt;p&gt;Rollback possible ✓&lt;/p&gt;

&lt;p&gt;This part is much less exciting than generating a beautiful application from a prompt.&lt;/p&gt;

&lt;p&gt;Unfortunately, users don't care which part was exciting.&lt;/p&gt;

&lt;p&gt;They care that the software works.&lt;/p&gt;

&lt;p&gt;AI can also create operational debt faster&lt;/p&gt;

&lt;p&gt;There's another side to faster development.&lt;/p&gt;

&lt;p&gt;When building becomes easier, it becomes easier to build more.&lt;/p&gt;

&lt;p&gt;More features.&lt;/p&gt;

&lt;p&gt;More endpoints.&lt;/p&gt;

&lt;p&gt;More dependencies.&lt;/p&gt;

&lt;p&gt;More services.&lt;/p&gt;

&lt;p&gt;More database tables.&lt;/p&gt;

&lt;p&gt;More infrastructure.&lt;/p&gt;

&lt;p&gt;And every additional moving part becomes something that might eventually need to be operated.&lt;/p&gt;

&lt;p&gt;You can create complexity faster than you can understand it.&lt;/p&gt;

&lt;p&gt;That's an interesting problem.&lt;/p&gt;

&lt;p&gt;A developer might previously spend three days implementing a feature and naturally become familiar with almost every part of it.&lt;/p&gt;

&lt;p&gt;Now an AI agent might generate the same feature in twenty minutes.&lt;/p&gt;

&lt;p&gt;The output arrived faster.&lt;/p&gt;

&lt;p&gt;The developer's mental model did not necessarily accelerate at the same rate.&lt;/p&gt;

&lt;p&gt;That gap matters when something breaks.&lt;/p&gt;

&lt;p&gt;This changes what developers need to get good at&lt;/p&gt;

&lt;p&gt;I don't think the lesson is that developers should stop using AI.&lt;/p&gt;

&lt;p&gt;Quite the opposite.&lt;/p&gt;

&lt;p&gt;The productivity increase is too useful to ignore.&lt;/p&gt;

&lt;p&gt;But our skills probably need to move upward.&lt;/p&gt;

&lt;p&gt;Knowing how to generate code becomes less valuable than knowing how to evaluate it.&lt;/p&gt;

&lt;p&gt;Questions like:&lt;/p&gt;

&lt;p&gt;Is this architecture appropriate?&lt;br&gt;
What assumptions does this code make?&lt;br&gt;
What happens when this fails?&lt;br&gt;
Is this secure?&lt;br&gt;
How will I observe this in production?&lt;br&gt;
How do I recover from failure?&lt;br&gt;
What happens at 100x the current traffic?&lt;br&gt;
What dependencies am I introducing?&lt;/p&gt;

&lt;p&gt;Those questions become increasingly important.&lt;/p&gt;

&lt;p&gt;The job starts moving from:&lt;/p&gt;

&lt;p&gt;How do I write this?&lt;/p&gt;

&lt;p&gt;toward:&lt;/p&gt;

&lt;p&gt;How do I know this is actually good?&lt;/p&gt;

&lt;p&gt;That is a much more interesting engineering problem.&lt;/p&gt;

&lt;p&gt;Production is where abstractions meet reality&lt;/p&gt;

&lt;p&gt;Development tools can hide an enormous amount of complexity.&lt;/p&gt;

&lt;p&gt;And that's usually a good thing.&lt;/p&gt;

&lt;p&gt;Good abstractions let us build faster.&lt;/p&gt;

&lt;p&gt;But production eventually exposes whatever the abstraction didn't cover.&lt;/p&gt;

&lt;p&gt;Your database doesn't care how quickly the application was generated when a migration locks a table.&lt;/p&gt;

&lt;p&gt;Your server doesn't care that an AI wrote the endpoint when memory usage keeps growing.&lt;/p&gt;

&lt;p&gt;Your users don't care how impressive the development process was when authentication goes down.&lt;/p&gt;

&lt;p&gt;Reality remains annoyingly traditional.&lt;/p&gt;

&lt;p&gt;Networks fail.&lt;/p&gt;

&lt;p&gt;Disks fill.&lt;/p&gt;

&lt;p&gt;Processes crash.&lt;/p&gt;

&lt;p&gt;Humans make mistakes.&lt;/p&gt;

&lt;p&gt;Dependencies break.&lt;/p&gt;

&lt;p&gt;Credentials expire.&lt;/p&gt;

&lt;p&gt;And software has to deal with all of it.&lt;/p&gt;

&lt;p&gt;The next generation of developer tooling will probably move downstream&lt;/p&gt;

&lt;p&gt;We've spent the last few years making creation dramatically easier.&lt;/p&gt;

&lt;p&gt;AI coding agents.&lt;/p&gt;

&lt;p&gt;App generators.&lt;/p&gt;

&lt;p&gt;Better frameworks.&lt;/p&gt;

&lt;p&gt;Managed databases.&lt;/p&gt;

&lt;p&gt;Backend-as-a-service platforms.&lt;/p&gt;

&lt;p&gt;Component libraries.&lt;/p&gt;

&lt;p&gt;All of these reduce the time between idea and working software.&lt;/p&gt;

&lt;p&gt;The next major opportunity may be making everything after creation equally simple.&lt;/p&gt;

&lt;p&gt;Not just:&lt;/p&gt;

&lt;p&gt;Build&lt;/p&gt;

&lt;p&gt;but:&lt;/p&gt;

&lt;p&gt;Verify → Deploy → Operate → Recover&lt;/p&gt;

&lt;p&gt;Because if AI keeps making software creation faster, developers are going to produce more software.&lt;/p&gt;

&lt;p&gt;And all of that software eventually has to run somewhere.&lt;/p&gt;

&lt;p&gt;The interesting question isn't “Can AI build this?”&lt;/p&gt;

&lt;p&gt;We're rapidly reaching a point where the answer to that question is often:&lt;/p&gt;

&lt;p&gt;Probably.&lt;/p&gt;

&lt;p&gt;The more useful questions are becoming:&lt;/p&gt;

&lt;p&gt;Should we build it this way?&lt;/p&gt;

&lt;p&gt;Is it ready for production?&lt;/p&gt;

&lt;p&gt;Can we understand what happens when it fails?&lt;/p&gt;

&lt;p&gt;Can we safely operate it?&lt;/p&gt;

&lt;p&gt;Can we recover?&lt;/p&gt;

&lt;p&gt;The impressive part of software development is becoming easier.&lt;/p&gt;

&lt;p&gt;The boring part is becoming the bottleneck.&lt;/p&gt;

&lt;p&gt;And ironically, the boring part is usually what determines whether the software actually survives.&lt;/p&gt;

&lt;p&gt;Has AI made you ship faster, or has it just moved the difficult part of your work somewhere else?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>Writing About Code Has Made Me Better at Writing Code</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Wed, 30 Sep 2026 08:07:50 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/writing-about-code-has-made-me-better-at-writing-code-3843</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/writing-about-code-has-made-me-better-at-writing-code-3843</guid>
      <description>&lt;p&gt;Most developers treat technical writing as something you do after you know what you're talking about.&lt;/p&gt;

&lt;p&gt;I've started thinking about it the other way around.&lt;/p&gt;

&lt;p&gt;Sometimes I don't realize how poorly I understand something until I try to explain it.&lt;/p&gt;

&lt;p&gt;You can spend hours working with a piece of technology and feel completely comfortable with it.&lt;/p&gt;

&lt;p&gt;Then somebody asks:&lt;/p&gt;

&lt;p&gt;Why does it work that way?&lt;/p&gt;

&lt;p&gt;And suddenly your brain goes:&lt;/p&gt;

&lt;p&gt;Well… it just does.&lt;/p&gt;

&lt;p&gt;That's usually the point where you discover the difference between recognizing something and actually understanding it.&lt;/p&gt;

&lt;p&gt;Code lets you hide behind working software&lt;/p&gt;

&lt;p&gt;Programming has an interesting loophole.&lt;/p&gt;

&lt;p&gt;Your code can work even when your mental model is incomplete.&lt;/p&gt;

&lt;p&gt;You can know that:&lt;/p&gt;

&lt;p&gt;await db.query(...)&lt;/p&gt;

&lt;p&gt;works.&lt;/p&gt;

&lt;p&gt;You can know where to put it.&lt;/p&gt;

&lt;p&gt;You can know how to handle the response.&lt;/p&gt;

&lt;p&gt;But that doesn't necessarily mean you understand:&lt;/p&gt;

&lt;p&gt;what happens when the connection drops&lt;br&gt;
how the connection pool behaves&lt;br&gt;
when the promise resolves&lt;br&gt;
what happens under concurrent requests&lt;br&gt;
how the database handles the query&lt;br&gt;
what failure actually looks like&lt;/p&gt;

&lt;p&gt;And that's fine.&lt;/p&gt;

&lt;p&gt;Nobody needs to understand every abstraction underneath every line of code they write.&lt;/p&gt;

&lt;p&gt;But the gaps become obvious when you attempt to explain the system to another person.&lt;/p&gt;

&lt;p&gt;Writing removes the autocomplete from your understanding.&lt;/p&gt;

&lt;p&gt;Explaining something forces you to find the missing pieces&lt;/p&gt;

&lt;p&gt;Let's say I want to write:&lt;/p&gt;

&lt;p&gt;"A database connection pool improves performance."&lt;/p&gt;

&lt;p&gt;Easy sentence.&lt;/p&gt;

&lt;p&gt;Then I ask myself:&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because we're reusing connections instead of creating one for every request.&lt;/p&gt;

&lt;p&gt;Okay.&lt;/p&gt;

&lt;p&gt;Why is opening a new database connection expensive?&lt;/p&gt;

&lt;p&gt;Now we're talking about networking, authentication, resource allocation and connection setup.&lt;/p&gt;

&lt;p&gt;What happens if the pool is exhausted?&lt;/p&gt;

&lt;p&gt;What decides how many connections should exist?&lt;/p&gt;

&lt;p&gt;Can too many connections make performance worse instead of better?&lt;/p&gt;

&lt;p&gt;Five minutes ago I thought I was writing one paragraph.&lt;/p&gt;

&lt;p&gt;Now I'm reading PostgreSQL documentation.&lt;/p&gt;

&lt;p&gt;That's what I like about writing.&lt;/p&gt;

&lt;p&gt;It exposes the edges of what you actually know.&lt;/p&gt;

&lt;p&gt;Tutorials are especially unforgiving&lt;/p&gt;

&lt;p&gt;When you're coding alone, you can skip steps mentally.&lt;/p&gt;

&lt;p&gt;You know that before this:&lt;/p&gt;

&lt;p&gt;npm run dev&lt;/p&gt;

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

&lt;p&gt;npm install&lt;/p&gt;

&lt;p&gt;created .env, configured the database and probably fixed three errors you no longer remember.&lt;/p&gt;

&lt;p&gt;The reader doesn't know any of that.&lt;/p&gt;

&lt;p&gt;So when you're writing a tutorial, you have to reconstruct the real sequence.&lt;/p&gt;

&lt;p&gt;That forces you to think more carefully about:&lt;/p&gt;

&lt;p&gt;dependencies&lt;br&gt;
assumptions&lt;br&gt;
defaults&lt;br&gt;
edge cases&lt;br&gt;
setup&lt;br&gt;
failure states&lt;/p&gt;

&lt;p&gt;You begin noticing complexity you've learned to ignore.&lt;/p&gt;

&lt;p&gt;I've had several situations where writing about a workflow made me think:&lt;/p&gt;

&lt;p&gt;Why does this require so many steps in the first place?&lt;/p&gt;

&lt;p&gt;Which is also where product ideas tend to appear.&lt;/p&gt;

&lt;p&gt;Writing is surprisingly good product research&lt;/p&gt;

&lt;p&gt;This one wasn't obvious to me initially.&lt;/p&gt;

&lt;p&gt;When you try to explain a technical problem properly, you naturally start researching how other developers solve it.&lt;/p&gt;

&lt;p&gt;Suppose I'm writing about deploying a backend.&lt;/p&gt;

&lt;p&gt;I might start with:&lt;/p&gt;

&lt;p&gt;"Deploying a backend requires…"&lt;/p&gt;

&lt;p&gt;Then I need examples.&lt;/p&gt;

&lt;p&gt;So I start looking at:&lt;/p&gt;

&lt;p&gt;Docker&lt;br&gt;
Railway&lt;br&gt;
Render&lt;br&gt;
VPS deployments&lt;br&gt;
CI/CD&lt;br&gt;
migrations&lt;br&gt;
reverse proxies&lt;br&gt;
environment management&lt;br&gt;
health checks&lt;/p&gt;

&lt;p&gt;And while reading, patterns start showing up.&lt;/p&gt;

&lt;p&gt;Everyone has slightly different solutions to the same underlying problems.&lt;/p&gt;

&lt;p&gt;You start noticing where tools overlap.&lt;/p&gt;

&lt;p&gt;Where workflows are unnecessarily complicated.&lt;/p&gt;

&lt;p&gt;Where developers complain.&lt;/p&gt;

&lt;p&gt;Where documentation repeatedly has to explain the same thing.&lt;/p&gt;

&lt;p&gt;You weren't doing market research.&lt;/p&gt;

&lt;p&gt;You were trying to write a decent article.&lt;/p&gt;

&lt;p&gt;But you end up learning a lot about the market anyway.&lt;/p&gt;

&lt;p&gt;Writing also generates ideas&lt;/p&gt;

&lt;p&gt;One article tends to create five more.&lt;/p&gt;

&lt;p&gt;You start writing about logging.&lt;/p&gt;

&lt;p&gt;Then you realize error reporting deserves its own article.&lt;/p&gt;

&lt;p&gt;That leads to observability.&lt;/p&gt;

&lt;p&gt;Then production debugging.&lt;/p&gt;

&lt;p&gt;Then distributed tracing.&lt;/p&gt;

&lt;p&gt;Then suddenly you have a backlog of topics you didn't have yesterday.&lt;/p&gt;

&lt;p&gt;It's basically recursion for ideas.&lt;/p&gt;

&lt;p&gt;The most useful part is that the ideas aren't coming from:&lt;/p&gt;

&lt;p&gt;"What should I post today?"&lt;/p&gt;

&lt;p&gt;They're coming from questions you naturally encountered while trying to understand something.&lt;/p&gt;

&lt;p&gt;Those tend to make better articles.&lt;/p&gt;

&lt;p&gt;It changes how you read documentation&lt;/p&gt;

&lt;p&gt;Before I started writing more often, documentation was usually transactional.&lt;/p&gt;

&lt;p&gt;I had a problem.&lt;/p&gt;

&lt;p&gt;I searched for the answer.&lt;/p&gt;

&lt;p&gt;I copied the relevant mental model into my brain.&lt;/p&gt;

&lt;p&gt;Then I left.&lt;/p&gt;

&lt;p&gt;Now I notice more things.&lt;/p&gt;

&lt;p&gt;Why did the documentation explain this concept before that one?&lt;/p&gt;

&lt;p&gt;What misconception are they trying to prevent?&lt;/p&gt;

&lt;p&gt;Why is this warning here?&lt;/p&gt;

&lt;p&gt;What edge case was common enough that somebody decided it deserved an entire section?&lt;/p&gt;

&lt;p&gt;Good technical documentation isn't only teaching you the technology.&lt;/p&gt;

&lt;p&gt;It's showing you where people usually misunderstand it.&lt;/p&gt;

&lt;p&gt;That information is incredibly useful.&lt;/p&gt;

&lt;p&gt;It makes vague opinions harder to keep&lt;/p&gt;

&lt;p&gt;Developers have plenty of opinions.&lt;/p&gt;

&lt;p&gt;"This framework is slow."&lt;/p&gt;

&lt;p&gt;"Microservices are overkill."&lt;/p&gt;

&lt;p&gt;"SQL is better."&lt;/p&gt;

&lt;p&gt;"Serverless is expensive."&lt;/p&gt;

&lt;p&gt;"X is easier than Y."&lt;/p&gt;

&lt;p&gt;Those opinions are easy to say.&lt;/p&gt;

&lt;p&gt;They're much harder to publish when you know another developer can ask:&lt;/p&gt;

&lt;p&gt;Based on what?&lt;/p&gt;

&lt;p&gt;Writing forces you to qualify things.&lt;/p&gt;

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

&lt;p&gt;Serverless is expensive.&lt;/p&gt;

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

&lt;p&gt;Serverless can become expensive for workloads with these characteristics…&lt;/p&gt;

&lt;p&gt;Which is a much better statement.&lt;/p&gt;

&lt;p&gt;You stop trying to sound certain and start trying to be accurate.&lt;/p&gt;

&lt;p&gt;That's useful far beyond writing.&lt;/p&gt;

&lt;p&gt;You don't have to become a "content creator"&lt;/p&gt;

&lt;p&gt;This is probably the part developers hate.&lt;/p&gt;

&lt;p&gt;The moment somebody says:&lt;/p&gt;

&lt;p&gt;You should write.&lt;/p&gt;

&lt;p&gt;it starts sounding like:&lt;/p&gt;

&lt;p&gt;Build your personal brand. Post three times every day. Optimize engagement.&lt;/p&gt;

&lt;p&gt;I'm not talking about that.&lt;/p&gt;

&lt;p&gt;You can write once a month.&lt;/p&gt;

&lt;p&gt;You can have 20 readers.&lt;/p&gt;

&lt;p&gt;You can publish something that gets absolutely no traction.&lt;/p&gt;

&lt;p&gt;The value isn't only distribution.&lt;/p&gt;

&lt;p&gt;The process itself is useful.&lt;/p&gt;

&lt;p&gt;You are forcing your brain to turn:&lt;/p&gt;

&lt;p&gt;I think I understand this&lt;/p&gt;

&lt;p&gt;into:&lt;/p&gt;

&lt;p&gt;Here is exactly how I think this works.&lt;/p&gt;

&lt;p&gt;Those are very different things.&lt;/p&gt;

&lt;p&gt;The simplest writing habit I've found&lt;/p&gt;

&lt;p&gt;When something makes me stop while coding, I write it down.&lt;/p&gt;

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

&lt;p&gt;Things like:&lt;/p&gt;

&lt;p&gt;Why did this migration fail?&lt;/p&gt;

&lt;p&gt;Why does this API behave differently in production?&lt;/p&gt;

&lt;p&gt;Why are these two tools solving the same problem differently?&lt;/p&gt;

&lt;p&gt;Why does this configuration exist?&lt;/p&gt;

&lt;p&gt;What actually happens when this command runs?&lt;/p&gt;

&lt;p&gt;You don't need the article immediately.&lt;/p&gt;

&lt;p&gt;Just keep the questions.&lt;/p&gt;

&lt;p&gt;When you eventually sit down to write, you're not staring at a blank editor trying to invent a topic.&lt;/p&gt;

&lt;p&gt;You're answering something that genuinely made you curious.&lt;/p&gt;

&lt;p&gt;And curiosity is usually a better starting point than a content calendar.&lt;/p&gt;

&lt;p&gt;Writing is part of learning&lt;/p&gt;

&lt;p&gt;I used to think the sequence was:&lt;/p&gt;

&lt;p&gt;Learn → understand → write&lt;/p&gt;

&lt;p&gt;Now I think it's closer to:&lt;/p&gt;

&lt;p&gt;Learn → write → discover what you don't understand → learn more → rewrite&lt;/p&gt;

&lt;p&gt;And the loop keeps going.&lt;/p&gt;

&lt;p&gt;The article is almost a side effect.&lt;/p&gt;

&lt;p&gt;The real output is a clearer mental model.&lt;/p&gt;

&lt;p&gt;So if you're learning something, building something or repeatedly solving the same technical problem, try writing about it.&lt;/p&gt;

&lt;p&gt;Even if nobody reads it.&lt;/p&gt;

&lt;p&gt;You'll probably discover something you didn't know you didn't know.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>writing</category>
      <category>learning</category>
    </item>
    <item>
      <title>Your Backend Works Locally. That Doesn’t Mean It’s Production-Ready</title>
      <dc:creator>The Saint</dc:creator>
      <pubDate>Tue, 29 Sep 2026 00:07:56 +0000</pubDate>
      <link>https://dev.to/the_saint_dac63e343ee8704/your-backend-works-locally-that-doesnt-mean-its-production-ready-5g7l</link>
      <guid>https://dev.to/the_saint_dac63e343ee8704/your-backend-works-locally-that-doesnt-mean-its-production-ready-5g7l</guid>
      <description>&lt;p&gt;You finish the feature.&lt;/p&gt;

&lt;p&gt;The API responds correctly.&lt;/p&gt;

&lt;p&gt;The database connection works.&lt;/p&gt;

&lt;p&gt;Authentication passes.&lt;/p&gt;

&lt;p&gt;You run the app locally for the tenth time and everything looks fine.&lt;/p&gt;

&lt;p&gt;Then someone says:&lt;/p&gt;

&lt;p&gt;“Cool. Let’s deploy it.”&lt;/p&gt;

&lt;p&gt;And suddenly the problem changes completely.&lt;/p&gt;

&lt;p&gt;A backend that works on your laptop is not necessarily a backend that is ready for production.&lt;/p&gt;

&lt;p&gt;Local development proves that your application can run in your environment.&lt;/p&gt;

&lt;p&gt;Production asks a much harder question:&lt;/p&gt;

&lt;p&gt;Can this thing keep working when the environment is no longer under your control?&lt;/p&gt;

&lt;p&gt;That difference is where a lot of small teams get caught.&lt;/p&gt;

&lt;p&gt;Local development hides a lot&lt;/p&gt;

&lt;p&gt;Your development machine is unusually forgiving.&lt;/p&gt;

&lt;p&gt;You probably have:&lt;/p&gt;

&lt;p&gt;the correct environment variables&lt;br&gt;
direct access to the database&lt;br&gt;
the right runtime installed&lt;br&gt;
every dependency available&lt;br&gt;
full filesystem permissions&lt;br&gt;
a predictable network&lt;br&gt;
logs directly in your terminal&lt;br&gt;
one developer making requests&lt;br&gt;
a database you can reset without consequences&lt;/p&gt;

&lt;p&gt;Production gives you none of those guarantees.&lt;/p&gt;

&lt;p&gt;The application now needs to survive restarts, failed migrations, missing secrets, expired credentials, network interruptions, disk limits, bad deployments and actual users.&lt;/p&gt;

&lt;p&gt;That means “it runs locally” is only the beginning.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configuration becomes infrastructure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Locally, this might be enough:&lt;/p&gt;

&lt;p&gt;DATABASE_URL=postgresql://localhost:5432/app&lt;br&gt;
JWT_SECRET=dev-secret&lt;br&gt;
PORT=3000&lt;/p&gt;

&lt;p&gt;In production, configuration has consequences.&lt;/p&gt;

&lt;p&gt;What happens if JWT_SECRET is missing?&lt;/p&gt;

&lt;p&gt;Does the application refuse to start?&lt;/p&gt;

&lt;p&gt;Or does it silently fall back to something unsafe?&lt;/p&gt;

&lt;p&gt;What happens when:&lt;/p&gt;

&lt;p&gt;process.env.DATABASE_URL&lt;/p&gt;

&lt;p&gt;is undefined?&lt;/p&gt;

&lt;p&gt;A production backend should fail predictably.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;const required = [&lt;br&gt;
  "DATABASE_URL",&lt;br&gt;
  "JWT_SECRET"&lt;br&gt;
];&lt;/p&gt;

&lt;p&gt;for (const variable of required) {&lt;br&gt;
  if (!process.env[variable]) {&lt;br&gt;
    throw new Error(&lt;br&gt;
      &lt;code&gt;Missing required environment variable: ${variable}&lt;/code&gt;&lt;br&gt;
    );&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;It is simple, but it is much better than discovering the problem after deployment.&lt;/p&gt;

&lt;p&gt;Configuration should be validated before the application begins accepting traffic.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Database migrations stop being harmless&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;During development, database changes are easy.&lt;/p&gt;

&lt;p&gt;Something breaks?&lt;/p&gt;

&lt;p&gt;Reset the database.&lt;/p&gt;

&lt;p&gt;Change the schema.&lt;/p&gt;

&lt;p&gt;Run the migration again.&lt;/p&gt;

&lt;p&gt;Move on.&lt;/p&gt;

&lt;p&gt;Production is different because the database contains data you cannot casually destroy.&lt;/p&gt;

&lt;p&gt;Imagine changing:&lt;/p&gt;

&lt;p&gt;ALTER TABLE users&lt;br&gt;
DROP COLUMN username;&lt;/p&gt;

&lt;p&gt;locally.&lt;/p&gt;

&lt;p&gt;No big deal.&lt;/p&gt;

&lt;p&gt;Now imagine doing that with 50,000 production users and discovering another part of the application still depends on username.&lt;/p&gt;

&lt;p&gt;The migration technically succeeded.&lt;/p&gt;

&lt;p&gt;The deployment still failed.&lt;/p&gt;

&lt;p&gt;Production migrations need to consider:&lt;/p&gt;

&lt;p&gt;backwards compatibility&lt;br&gt;
existing data&lt;br&gt;
rollback strategy&lt;br&gt;
application version compatibility&lt;br&gt;
long-running operations&lt;br&gt;
database locks&lt;/p&gt;

&lt;p&gt;For risky changes, the safer pattern is often incremental.&lt;/p&gt;

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

&lt;p&gt;remove old field&lt;br&gt;
deploy new code&lt;br&gt;
hope&lt;/p&gt;

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

&lt;p&gt;add new field&lt;br&gt;
deploy compatible code&lt;br&gt;
migrate data&lt;br&gt;
verify&lt;br&gt;
remove old field later&lt;/p&gt;

&lt;p&gt;The boring version is usually the safer version.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;“The server is running” is not a health check&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your process can be alive while your application is effectively dead.&lt;/p&gt;

&lt;p&gt;Maybe:&lt;/p&gt;

&lt;p&gt;PostgreSQL is unreachable&lt;br&gt;
Redis is unavailable&lt;br&gt;
migrations failed&lt;br&gt;
storage credentials are invalid&lt;br&gt;
an external dependency is timing out&lt;/p&gt;

&lt;p&gt;Your process manager may still happily report:&lt;/p&gt;

&lt;p&gt;RUNNING&lt;/p&gt;

&lt;p&gt;That is why production systems usually need an actual health endpoint.&lt;/p&gt;

&lt;p&gt;A simple example:&lt;/p&gt;

&lt;p&gt;app.get("/health", async (req, res) =&amp;gt; {&lt;br&gt;
  try {&lt;br&gt;
    await db.query("SELECT 1");&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;res.status(200).json({
  status: "healthy"
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} catch {&lt;br&gt;
    res.status(503).json({&lt;br&gt;
      status: "unhealthy"&lt;br&gt;
    });&lt;br&gt;
  }&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;Now your infrastructure has something meaningful to test.&lt;/p&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;p&gt;Is Node.js still running?&lt;/p&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;p&gt;Can this application actually serve requests?&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Logging changes when nobody is watching the terminal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;During development:&lt;/p&gt;

&lt;p&gt;console.log(error);&lt;/p&gt;

&lt;p&gt;often feels perfectly adequate.&lt;/p&gt;

&lt;p&gt;You are already looking at the terminal.&lt;/p&gt;

&lt;p&gt;Production errors usually happen while nobody is looking.&lt;/p&gt;

&lt;p&gt;And this:&lt;/p&gt;

&lt;p&gt;Something went wrong&lt;/p&gt;

&lt;p&gt;is almost useless three hours later.&lt;/p&gt;

&lt;p&gt;Useful production logs should tell you things like:&lt;/p&gt;

&lt;p&gt;what happened&lt;br&gt;
when it happened&lt;br&gt;
which request triggered it&lt;br&gt;
which service failed&lt;br&gt;
how severe the failure was&lt;/p&gt;

&lt;p&gt;Structured logs are much easier to search and process.&lt;/p&gt;

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

&lt;p&gt;console.log("Database error");&lt;/p&gt;

&lt;p&gt;something closer to:&lt;/p&gt;

&lt;p&gt;logger.error({&lt;br&gt;
  event: "database_connection_failed",&lt;br&gt;
  requestId,&lt;br&gt;
  error: error.message&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;gives you something you can actually investigate.&lt;/p&gt;

&lt;p&gt;Production observability is not about printing more text.&lt;/p&gt;

&lt;p&gt;It is about leaving enough evidence to understand failures after they happen.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Backups do not matter until restoration works&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A lot of systems technically have backups.&lt;/p&gt;

&lt;p&gt;Far fewer know whether those backups can actually be restored.&lt;/p&gt;

&lt;p&gt;There is an uncomfortable difference between:&lt;/p&gt;

&lt;p&gt;“We create a backup every night.”&lt;/p&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;p&gt;“We successfully restored yesterday’s backup into a clean database.”&lt;/p&gt;

&lt;p&gt;The second statement proves something.&lt;/p&gt;

&lt;p&gt;The first only proves that a file exists somewhere.&lt;/p&gt;

&lt;p&gt;A production backup strategy should answer:&lt;/p&gt;

&lt;p&gt;How often do backups run?&lt;br&gt;
Where are they stored?&lt;br&gt;
How long are they retained?&lt;br&gt;
Are they encrypted?&lt;br&gt;
How quickly can they be restored?&lt;br&gt;
When was restoration last tested?&lt;/p&gt;

&lt;p&gt;A backup you have never restored is still partly a hypothesis.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deployment without rollback is a one-way door&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Imagine deploying version 1.8.&lt;/p&gt;

&lt;p&gt;Ten minutes later:&lt;/p&gt;

&lt;p&gt;error rates spike&lt;br&gt;
users cannot authenticate&lt;br&gt;
database queries slow down&lt;br&gt;
one endpoint starts returning 500&lt;/p&gt;

&lt;p&gt;What happens next?&lt;/p&gt;

&lt;p&gt;If the answer is:&lt;/p&gt;

&lt;p&gt;“Give me twenty minutes while I fix it.”&lt;/p&gt;

&lt;p&gt;you do not really have a recovery strategy.&lt;/p&gt;

&lt;p&gt;Sometimes the fastest fix is simply returning to the last known-good version.&lt;/p&gt;

&lt;p&gt;That requires your deployment system to know:&lt;/p&gt;

&lt;p&gt;v1.6&lt;br&gt;
v1.7&lt;br&gt;
v1.8 ← current&lt;/p&gt;

&lt;p&gt;and to make returning to:&lt;/p&gt;

&lt;p&gt;v1.7&lt;/p&gt;

&lt;p&gt;boring.&lt;/p&gt;

&lt;p&gt;Production reliability is not about never breaking anything.&lt;/p&gt;

&lt;p&gt;Eventually something will break.&lt;/p&gt;

&lt;p&gt;Reliability is largely about reducing how expensive failure becomes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Production traffic exposes assumptions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;During local development you might be the only user.&lt;/p&gt;

&lt;p&gt;Production introduces:&lt;/p&gt;

&lt;p&gt;User 1&lt;br&gt;
User 2&lt;br&gt;
User 3&lt;br&gt;
...&lt;br&gt;
User 10,000&lt;/p&gt;

&lt;p&gt;And all of them can do things at inconvenient times.&lt;/p&gt;

&lt;p&gt;You start encountering problems like:&lt;/p&gt;

&lt;p&gt;race conditions&lt;br&gt;
exhausted database connections&lt;br&gt;
duplicate requests&lt;br&gt;
slow queries&lt;br&gt;
rate abuse&lt;br&gt;
concurrent writes&lt;br&gt;
memory growth&lt;br&gt;
unexpectedly large payloads&lt;/p&gt;

&lt;p&gt;For example, this query may seem harmless:&lt;/p&gt;

&lt;p&gt;SELECT *&lt;br&gt;
FROM orders;&lt;/p&gt;

&lt;p&gt;until orders contains several million records.&lt;/p&gt;

&lt;p&gt;Production has a habit of turning assumptions into benchmarks.&lt;/p&gt;

&lt;p&gt;A better definition of “done”&lt;/p&gt;

&lt;p&gt;For a backend feature, “done” should probably not mean:&lt;/p&gt;

&lt;p&gt;✓ Works locally&lt;/p&gt;

&lt;p&gt;A stronger checklist is closer to:&lt;/p&gt;

&lt;p&gt;✓ Works locally&lt;br&gt;
✓ Configuration validated&lt;br&gt;
✓ Database changes are safe&lt;br&gt;
✓ Authentication and permissions verified&lt;br&gt;
✓ Health checks available&lt;br&gt;
✓ Errors observable&lt;br&gt;
✓ Deployment reproducible&lt;br&gt;
✓ Backups exist&lt;br&gt;
✓ Restoration tested&lt;br&gt;
✓ Rollback possible&lt;br&gt;
✓ Production behavior monitored&lt;/p&gt;

&lt;p&gt;Not every side project needs enterprise infrastructure.&lt;/p&gt;

&lt;p&gt;But every application should know what level of failure it is prepared to tolerate.&lt;/p&gt;

&lt;p&gt;That is the important part.&lt;/p&gt;

&lt;p&gt;The backend lifecycle is bigger than building&lt;/p&gt;

&lt;p&gt;Developers spend a lot of time discussing how quickly we can create APIs, databases and authentication.&lt;/p&gt;

&lt;p&gt;Those things matter.&lt;/p&gt;

&lt;p&gt;But building the backend is only one stage.&lt;/p&gt;

&lt;p&gt;Eventually the lifecycle becomes:&lt;/p&gt;

&lt;p&gt;Build → Verify → Deploy → Operate → Own&lt;/p&gt;

&lt;p&gt;And the transitions between those stages are where a surprising amount of engineering time disappears.&lt;/p&gt;

&lt;p&gt;That is also the problem we have been thinking about while building CrescoDB: how to give smaller engineering teams a cleaner path through the backend lifecycle without forcing them to stitch together an increasingly complicated production stack.&lt;/p&gt;

&lt;p&gt;But regardless of which tools you use, the principle stays the same:&lt;/p&gt;

&lt;p&gt;A backend isn't production-ready because it runs. It's production-ready when you understand how it fails.&lt;/p&gt;

&lt;p&gt;What is the one production check you wish you'd added before one of your deployments went wrong?&lt;/p&gt;

</description>
      <category>programming</category>
      <category>node</category>
      <category>devops</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
