<?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: Umadhar M</title>
    <description>The latest articles on DEV Community by Umadhar M (@umadhar).</description>
    <link>https://dev.to/umadhar</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2099602%2F2ae89a15-41d5-4b38-b8be-f0d847f246a1.png</url>
      <title>DEV Community: Umadhar M</title>
      <link>https://dev.to/umadhar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/umadhar"/>
    <language>en</language>
    <item>
      <title>Database Migrations &amp; Seeders: Stop Writing Manual SQL Scripts</title>
      <dc:creator>Umadhar M</dc:creator>
      <pubDate>Wed, 29 Apr 2026 18:06:36 +0000</pubDate>
      <link>https://dev.to/umadhar/database-migrations-seeders-stop-writing-manual-sql-scripts-1g8b</link>
      <guid>https://dev.to/umadhar/database-migrations-seeders-stop-writing-manual-sql-scripts-1g8b</guid>
      <description>&lt;p&gt;If you've ever said &lt;em&gt;"I forgot to run the ALTER TABLE on production"&lt;/em&gt; — this post is for you.&lt;/p&gt;

&lt;p&gt;Manual database changes are one of the most common causes of production incidents. A developer adds a column locally, merges the feature, deploys — and the app crashes because nobody ran the SQL on the live server.&lt;/p&gt;

&lt;p&gt;Migrations and seeders solve this completely. But the tooling is different for every stack, and most guides only cover one framework. This one covers &lt;strong&gt;all of them&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;Instead of running SQL manually, you write &lt;strong&gt;versioned migration files&lt;/strong&gt; as part of your normal code. They get reviewed in pull requests, committed to git, and run automatically during deployment. Nobody forgets a column again.&lt;/p&gt;

&lt;p&gt;Two concepts to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Migration&lt;/strong&gt; — a versioned script for schema changes (CREATE TABLE, ALTER, ADD COLUMN). Applied once, tracked by the framework.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seeder&lt;/strong&gt; — scripted insertion of reference or initial data. Always written to be idempotent — meaning it can run multiple times without duplicating data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Not Just Write the SQL Directly?
&lt;/h2&gt;

&lt;p&gt;Many teams start with raw SQL scripts shared over Slack or stored in a &lt;code&gt;scripts/&lt;/code&gt; folder. This breaks down fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No guarantee the script ran on every environment&lt;/li&gt;
&lt;li&gt;No rollback if something goes wrong&lt;/li&gt;
&lt;li&gt;No audit trail of who changed what and when&lt;/li&gt;
&lt;li&gt;New developers have no idea what order to run things in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Migration tools solve all of this by tracking every change in a dedicated table in your database. If a migration has already run, it skips it. If it hasn't, it applies it. Simple and reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Example — Python Flask (Alembic + SQLAlchemy)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;alembic sqlalchemy
alembic init migrations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# migrations/versions/xxxx_add_email_to_users.py
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;downgrade&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drop_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;users&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run it&lt;/span&gt;
alembic upgrade &lt;span class="nb"&gt;head&lt;/span&gt;

&lt;span class="c"&gt;# Check status&lt;/span&gt;
alembic current
alembic &lt;span class="nb"&gt;history&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seeder — always check before inserting to keep it idempotent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# seed.py
&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;role_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ADMIN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;USER&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;role_name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;role_name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Seeding complete.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python seed.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What About Existing Apps With No Migrations Yet?
&lt;/h2&gt;

&lt;p&gt;This is the most common question. If your app is already running:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Export your current schema as a &lt;strong&gt;baseline migration file&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Tell the migration tool to mark it as "already applied"&lt;/li&gt;
&lt;li&gt;From that point — all future changes go through migration files only&lt;/li&gt;
&lt;li&gt;Update your CI/CD pipeline to run migrations on every deploy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For Flask/Alembic specifically, use &lt;code&gt;alembic stamp head&lt;/code&gt; to mark the current state as the baseline without running anything. From that point forward, every schema change is a new migration file.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Full Guide
&lt;/h2&gt;

&lt;p&gt;The complete article covers all 7 frameworks with full code examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spring Boot&lt;/strong&gt; — Flyway + Hibernate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NodeJS&lt;/strong&gt; — Sequelize CLI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python/Django&lt;/strong&gt; — Built-in migrations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python/Flask &amp;amp; FastAPI&lt;/strong&gt; — Alembic + SQLAlchemy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Laravel&lt;/strong&gt; — Artisan migrations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core PHP&lt;/strong&gt; — Phinx&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flutter&lt;/strong&gt; — sqflite local DB versioning&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;CI/CD pipeline integration examples&lt;/li&gt;
&lt;li&gt;Post-deploy verification scripts for each stack&lt;/li&gt;
&lt;li&gt;Idempotency patterns for seeders (MySQL, PostgreSQL, SQLite)&lt;/li&gt;
&lt;li&gt;A complete FAQ on common migration mistakes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;Read the full guide here:&lt;/strong&gt; &lt;a href="https://www.thedevtools.online/blog/database-migrations-and-seeders-best-practices" rel="noopener noreferrer"&gt;Database Migrations &amp;amp; Seeders Best Practices — The DevTools Online&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I also built a free collection of developer utilities at &lt;a href="https://www.thedevtools.online" rel="noopener noreferrer"&gt;thedevtools.online&lt;/a&gt; — tools like SQL Formatter, JSON Formatter, JWT Decoder and 40+ more, all browser-based with no login required.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>devops</category>
      <category>webdev</category>
      <category>dbmigrations</category>
    </item>
    <item>
      <title>I Got Tired of Jumping Between 10 Different Sites Just to Format JSON — So I Built My Own</title>
      <dc:creator>Umadhar M</dc:creator>
      <pubDate>Thu, 19 Mar 2026 08:45:04 +0000</pubDate>
      <link>https://dev.to/umadhar/i-got-tired-of-jumping-between-10-different-sites-just-to-format-json-so-i-built-my-own-1kop</link>
      <guid>https://dev.to/umadhar/i-got-tired-of-jumping-between-10-different-sites-just-to-format-json-so-i-built-my-own-1kop</guid>
      <description>&lt;p&gt;Every developer knows the drill.&lt;/p&gt;

&lt;p&gt;You're deep in a debugging session. You need to quickly decode a Base64 string, or format a minified JSON blob so you can actually read it, or generate a UUID for a test fixture. You open a new tab, search for a tool, land on a site drowning in ads and cookie popups, paste your data in, copy the result, and switch back to your IDE.&lt;/p&gt;

&lt;p&gt;Two minutes gone. Flow broken.&lt;/p&gt;

&lt;p&gt;I did this dozens of times a day not only for the same but for many others. And one day I just decided — I'll build one place that has all of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.thedevtools.online" rel="noopener noreferrer"&gt;The DevTools Online&lt;/a&gt; is a free, browser-based collection of developer utilities. No login. No ads. No data sent to any server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kanjtbj0kv9ldmiriu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kanjtbj0kv9ldmiriu6.png" alt="TheDevTolls.Online Home page" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of the tools available:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JSON Formatter &amp;amp; Validator&lt;/strong&gt; — paste minified JSON, get clean indented output instantly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base64 Encoder/Decoder&lt;/strong&gt; — including image to Base64 conversion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UUID Generator &amp;amp; Validator&lt;/strong&gt; — v4 UUIDs, bulk generation, validation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JWT Decoder&lt;/strong&gt; — inspect token headers and payloads without any server call&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regex Tester &amp;amp; Generator&lt;/strong&gt; — test patterns with real-time match highlighting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL Formatter&lt;/strong&gt; — clean up messy query dumps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Beautifier&lt;/strong&gt; — HTML, CSS, and JavaScript formatting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRON Expression Parser &amp;amp; Creator&lt;/strong&gt; — finally understand what &lt;code&gt;*/5 * * * *&lt;/code&gt; means&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Password Generator &amp;amp; Strength Checker&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hash Generator&lt;/strong&gt; (MD5, SHA-1, SHA-256)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS Lookup &amp;amp; WHOIS&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timestamp Converter, Age Calculator, Unit Converter&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;And about 40 more...&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The One Thing I Cared Most About: Privacy
&lt;/h2&gt;

&lt;p&gt;A lot of developer tools silently send your input to their servers. That's fine for formatting public JSON, but not when you're pasting JWT tokens, API keys, database connection strings, or proprietary code snippets.&lt;/p&gt;

&lt;p&gt;Every single tool on The Dev Tools Online runs &lt;strong&gt;entirely in your browser&lt;/strong&gt;. There's no backend processing your input. Your data never leaves your device.&lt;/p&gt;

&lt;p&gt;I made this a non-negotiable from day one. It's why I built it myself instead of just bookmarking someone else's site.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Few Things I Learned Building This
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. PHP is still great for simple, fast tool sites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I went with a plain PHP + vanilla JS stack. No framework overhead, no build steps, no Node modules folder weighing 300MB. Pages load fast, the codebase is easy to maintain, and deployment is just an GitHub Actions push. Sometimes boring technology is the right choice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;.htaccess&lt;/code&gt; rewrite rules are both powerful and terrifying&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Getting clean URLs (&lt;code&gt;/json_formatter&lt;/code&gt; instead of &lt;code&gt;/json_formatter.php&lt;/code&gt;) while handling redirects, blocking sensitive directories, and managing cache headers — all in one file — taught me more about Apache than 3 years of casual use did.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Browser-based tools are harder to build than they look&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you can't offload anything to a server, you have to handle every edge case in JavaScript. Malformed JSON, circular references, files that are too large, encodings that break on emoji — all of it has to be caught and handled gracefully in the client. It's a fun constraint to work within.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Content matters as much as the tools themselves&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I initially launched with just the tools and minimal descriptions. Traffic was near zero. I've since added proper documentation to each tool — real examples, use cases, common gotchas. The difference in engagement has been noticeable.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Coming Next
&lt;/h2&gt;

&lt;p&gt;I'm actively working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;diff checker&lt;/strong&gt; improvement with side-by-side view&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XML to JSON&lt;/strong&gt; and &lt;strong&gt;YAML validator&lt;/strong&gt; improvements&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;curl command builder&lt;/strong&gt; (something I personally need all the time)&lt;/li&gt;
&lt;li&gt;Better mobile experience on tool pages&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;If you're constantly switching tabs for simple dev tasks, give it a try: &lt;strong&gt;&lt;a href="https://www.thedevtools.online" rel="noopener noreferrer"&gt;thedevtools.online&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Everything is free. Nothing requires an account. And if a tool you need is missing, hit the Contact page and let me know — I genuinely read every message.&lt;/p&gt;

&lt;p&gt;I'm Umadhar, a software developer from India. Happy to answer any questions in the comments about the build, the stack, or the tools themselves.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: webdev, javascript, php, productivity, opensource&lt;/em&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
