<?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>Forem: Paolo</title>
    <description>The latest articles on Forem by Paolo (@bellini).</description>
    <link>https://forem.com/bellini</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%2F3818540%2Fa7bb2486-3c7e-4957-b50a-227b2db41399.png</url>
      <title>Forem: Paolo</title>
      <link>https://forem.com/bellini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/bellini"/>
    <language>en</language>
    <item>
      <title>Everyone Can Vibe Code. Do You Know What's Under the Hood?</title>
      <dc:creator>Paolo</dc:creator>
      <pubDate>Tue, 14 Apr 2026 08:39:08 +0000</pubDate>
      <link>https://forem.com/bellini/everyone-can-vibe-code-do-you-know-whats-under-the-hood-21pl</link>
      <guid>https://forem.com/bellini/everyone-can-vibe-code-do-you-know-whats-under-the-hood-21pl</guid>
      <description>&lt;p&gt;&lt;strong&gt;A deep dive into Node.js internals — V8, libuv, Turbofan JIT, and why TypeScript is not just about DX.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the age of vibe coding — where anyone can spin up a full-stack app by chatting with an AI agent — I decided to go against the trend and look &lt;strong&gt;down&lt;/strong&gt;, not up.&lt;/p&gt;

&lt;p&gt;Who hasn't seen JavaScript code at this point? It's everywhere. But if today everyone can &lt;em&gt;create&lt;/em&gt; software, what sets us developers apart? I'd argue it's understanding &lt;strong&gt;why&lt;/strong&gt; things work, not just &lt;em&gt;that&lt;/em&gt; they work.&lt;/p&gt;

&lt;p&gt;So I spent a few days digging into TypeScript, Node.js, V8, and libuv at a low level. Here's what I found.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js: A Wikipedia Definition That Tells You Nothing
&lt;/h2&gt;

&lt;p&gt;We all know the textbook one:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Node.js is an open-source, cross-platform, event-driven JavaScript runtime built on Google Chrome's V8 engine.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Cool. But what does that actually mean? How does the most widely used language in the world get executed on a server?&lt;/p&gt;

&lt;h2&gt;
  
  
  It's C All the Way Down
&lt;/h2&gt;

&lt;p&gt;Here's the first thing that surprised me: &lt;strong&gt;Node.js is made of C and C++ programs&lt;/strong&gt;. And that's not a coincidence.&lt;/p&gt;

&lt;p&gt;JavaScript is a web scripting language — it was born inside the browser. By itself, it can't touch your filesystem, open a network socket, or spawn a process. It has no idea what an operating system even is.&lt;/p&gt;

&lt;p&gt;So how does it do all of that in Node? Two pieces of software make it happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;V8&lt;/strong&gt; — Google's JavaScript engine, written in C++. It parses, compiles, and executes your JS code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;libuv&lt;/strong&gt; — a C library that provides asynchronous I/O and acts as the bridge between JavaScript and the operating system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;V8 + libuv = Node.js. That's the whole recipe.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Single-Threaded" — But Not How You Think
&lt;/h2&gt;

&lt;p&gt;Yes, Node is single-threaded by design. Your JavaScript code runs on one thread — the &lt;strong&gt;event loop&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But Node is not limited to a single thread. Under the hood, libuv maintains a &lt;strong&gt;thread pool&lt;/strong&gt; (4 threads by default) to handle blocking I/O operations — things like filesystem reads, DNS lookups, and compression. These run in the background and return results to the event loop when they're done.&lt;/p&gt;

&lt;p&gt;On top of that, you can explicitly spawn &lt;strong&gt;worker threads&lt;/strong&gt; or &lt;strong&gt;child processes&lt;/strong&gt; when you need true parallelism.&lt;/p&gt;

&lt;p&gt;So the "Node is single-threaded" take is only half the story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why TypeScript Is Not Just a DX Choice
&lt;/h2&gt;

&lt;p&gt;In 2026, why would you still write plain JavaScript instead of TypeScript? Many teams adopt TS for the developer experience — autocompletion, refactoring, catching bugs before runtime. That's reason enough.&lt;/p&gt;

&lt;p&gt;But there's a &lt;strong&gt;deeper, performance-level argument&lt;/strong&gt; for TypeScript that most people never talk about. It comes down to how V8 compiles your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  V8's Compilation Pipeline
&lt;/h3&gt;

&lt;p&gt;When V8 runs your JavaScript, it doesn't just interpret it line by line. Here's the simplified flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ignition&lt;/strong&gt; (interpreter) — parses your code and generates bytecode. Fast startup, slow execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sparkplug&lt;/strong&gt; — a baseline compiler that produces slightly optimized machine code without heavy analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turbofan&lt;/strong&gt; (JIT compiler) — the heavy hitter. It monitors which functions are called often ("hot functions"), profiles their behavior, and compiles them into &lt;strong&gt;highly optimized machine code&lt;/strong&gt;, bypassing bytecode entirely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key: Turbofan's optimizations depend on &lt;strong&gt;type consistency&lt;/strong&gt;. If a function always receives the same shapes and types, V8 can make aggressive assumptions and generate fast machine code.&lt;/p&gt;

&lt;p&gt;But if the types change mid-execution? V8 has to &lt;strong&gt;deoptimize&lt;/strong&gt; — throw away the optimized code and fall back to the slower bytecode path. This is expensive.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code That Proves It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// GOOD: consistent types → Turbofan optimizes aggressively&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addNumbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// V8 sees this function called thousands of times&lt;/span&gt;
&lt;span class="c1"&gt;// always with numbers → marks it as "hot" → compiles to&lt;/span&gt;
&lt;span class="c1"&gt;// optimized machine code&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;addNumbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// always numbers ✓&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 javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// BAD: inconsistent types → deoptimization&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// V8 profiles this, assumes numbers, optimizes...&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&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="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...then this happens:&lt;/span&gt;
&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// string?! → DEOPTIMIZE&lt;/span&gt;
&lt;span class="c1"&gt;// V8 discards the optimized machine code,&lt;/span&gt;
&lt;span class="c1"&gt;// falls back to bytecode interpretation,&lt;/span&gt;
&lt;span class="c1"&gt;// and has to re-profile from scratch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With TypeScript, you're enforcing type consistency at the source level. This means V8's profiler encounters &lt;strong&gt;monomorphic call sites&lt;/strong&gt; — functions that always see the same types — far more often. Turbofan loves this. The result is faster, more stable machine code.&lt;/p&gt;

&lt;p&gt;So TypeScript isn't just about catching bugs. It's about giving the engine &lt;strong&gt;what it needs to go fast&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  TypeScript Helps Your AI Agent Too
&lt;/h3&gt;

&lt;p&gt;Here's a thought that feels very 2026: types don't just help &lt;em&gt;you&lt;/em&gt; — they help your &lt;strong&gt;AI coding agent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When an agent generates or modifies code in a typed codebase, the type system acts as a set of guardrails. The agent can't silently pass a string where a number is expected, or return an object with a missing field. The compiler catches it immediately. In an untyped codebase, that same mistake slips through silently and shows up as a runtime bug three days later.&lt;/p&gt;

&lt;p&gt;Types are essentially a &lt;strong&gt;contract&lt;/strong&gt; — for humans, for V8, and now for AI agents. The more explicit your codebase is about its intent, the more reliably any contributor (human or not) can work within it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Fundamentals Still Matter
&lt;/h2&gt;

&lt;p&gt;Understanding how things work under the hood isn't just intellectual curiosity. It changes how you write code and how you think about the infrastructure running it.&lt;/p&gt;

&lt;p&gt;When you know that libuv has a thread pool of 4, you understand why CPU-heavy tasks can block your Node process. When you know about Turbofan deoptimization, you think twice about writing functions with wildly inconsistent types.&lt;/p&gt;

&lt;p&gt;Whether it's you writing the code or your AI agent — understanding what's happening under the surface is what lets you make informed decisions, optimize where it matters, and justify your architectural choices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Node.js Isn't the Only Game in Town
&lt;/h2&gt;

&lt;p&gt;Node isn't the only JavaScript runtime out there. But one that caught my attention recently is &lt;strong&gt;&lt;a href="https://bun.com" rel="noopener noreferrer"&gt;Bun&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Bun is an all-in-one toolkit: runtime, test runner, bundler, and package manager — all packed into a single binary. It doesn't use V8. Instead, it's built on &lt;strong&gt;JavaScriptCore&lt;/strong&gt; (the engine behind Safari) and written in &lt;strong&gt;Zig&lt;/strong&gt;, a low-level systems language.&lt;/p&gt;

&lt;p&gt;The performance numbers are hard to ignore: startup times around 5ms (vs Node's ~50ms), HTTP throughput up to 2-3x higher, and package installs that are 6-10x faster than npm. It also runs TypeScript natively — no transpilation step needed.&lt;/p&gt;

&lt;p&gt;Is it mature enough to replace Node in production? For complex, battle-tested systems with deep dependency trees — probably not yet. But for new projects, CLI tools, or if you're curious and want to experiment with a modern frontend setup, Bun is absolutely worth a look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go Deeper
&lt;/h2&gt;

&lt;p&gt;There's a lot more I could cover — the event loop phases, the microtask queue, how &lt;code&gt;async/await&lt;/code&gt; interacts with libuv, V8's hidden classes and inline caches. If you have a few hours to spare, I'd encourage you to explore these topics. The deeper you go, the more the pieces connect — and the better engineer you become.&lt;/p&gt;

&lt;p&gt;In an era where everyone can generate code, understanding &lt;em&gt;what the code does at the machine level&lt;/em&gt; is what makes the difference.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this useful or have anything to add, drop a comment — I'd love to hear what low-level topic you'd dig into next.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>software</category>
    </item>
    <item>
      <title>I built a Laravel package to stop explaining my database to AI tools every single day</title>
      <dc:creator>Paolo</dc:creator>
      <pubDate>Wed, 11 Mar 2026 13:36:23 +0000</pubDate>
      <link>https://forem.com/bellini/i-built-a-laravel-package-to-stop-explaining-my-database-to-ai-tools-every-single-day-38hf</link>
      <guid>https://forem.com/bellini/i-built-a-laravel-package-to-stop-explaining-my-database-to-ai-tools-every-single-day-38hf</guid>
      <description>&lt;p&gt;Anyone who lives in the software world knows that, even in the AI era, every day we rely on packages built by other developers instead of coding everything from scratch — especially if we want to have a shot at respecting client deadlines, which are always set "a day before yesterday". I'm a Freelance Software Engineer, and like most of us, I had used hundreds of Laravel packages but had never built one myself.&lt;/p&gt;

&lt;p&gt;Recently, while working on a client project with a small team, I kept running into the same problem: understanding the database structure across new commits and features was painful, and every time someone changed a migration I had to re-explain the whole schema to my AI tools — what changed, what relationships existed, what columns were added. Repetitive, messy, and honestly quite annoying after the tenth time.&lt;/p&gt;

&lt;p&gt;I searched for a package that could help. I didn't find anything that fit my needs. So I did what every developer does when they can't find a solution: complain for a few days, then build it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a package, and why now
&lt;/h2&gt;

&lt;p&gt;Two reasons pushed me over the edge.&lt;/p&gt;

&lt;p&gt;The first was practical: I wanted the database structure to be always available as a single exportable file, something an AI agent could read immediately without requiring me to paste half the project into a prompt.&lt;/p&gt;

&lt;p&gt;The second was personal: I had been looking for an excuse to go deeper into how Laravel works internally. Building web applications is great, but at some point you want to understand what's happening under the hood, not just consume what the framework provides.&lt;/p&gt;

&lt;p&gt;So I opened a new repo, knowing that if I got stuck I could ask my friend Claude.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing before writing a single line
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of implementation, I did two things.&lt;/p&gt;

&lt;p&gt;First, I went to the Reddit community and asked what actually convinces developers to use a package. The answers were unsurprisingly consistent: clear documentation, a focused scope, good test coverage, a codebase that doesn't make you want to close the tab immediately. Nothing revolutionary, but hearing it directly from real users before building was valuable.&lt;/p&gt;

&lt;p&gt;Then I spent time studying other Laravel packages — not to copy them, but to understand what a package built for others to install actually looks like, because it's genuinely different from what you build for yourself. You can't hide the messy parts in a public repo.&lt;/p&gt;

&lt;p&gt;Only once the structure was clear in my head did I start implementing. I know AI could have designed it for me, but I didn't want that. I wanted to actually understand it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;My main goal was to keep things simple but extendable. Not a script that works for my specific database and breaks the moment someone has a different setup, but something the community could grow over time by adding new formats or integrations.&lt;/p&gt;

&lt;p&gt;Two design patterns ended up being a perfect fit for what I needed.&lt;/p&gt;

&lt;p&gt;The first is the Strategy Pattern, used for renderers. The package supports multiple output formats like Mermaid diagrams and dbdiagram syntax, and potentially more in the future. Instead of hardcoding the rendering logic inside the core, each format is a separate class implementing a common interface. Adding a new format in the future means adding a new class, nothing more. The core doesn't change.&lt;/p&gt;

&lt;p&gt;The second is the Template Pattern, used for the translation process. Even though the output formats are different, the overall process is always the same: analyze the schema, iterate through tables, process columns and relationships, generate the output. Defining this structure once and letting each renderer fill in only its specific parts kept everything consistent without duplicating logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using my friend Claude as a coding assistant
&lt;/h2&gt;

&lt;p&gt;Once the design was locked, I used Claude to speed up the implementation — specifically to write the renderer formats and unit tests. This is where having a clear architecture paid off immediately: with well-defined interfaces and a structured plan, the AI could produce solid code without going off the rails.&lt;/p&gt;

&lt;p&gt;To make sure it actually did, I set up a strict set of Composer scripts before writing any implementation: linting, static analysis, 100% type coverage, 100% code coverage, and an auto-refactoring check. Every output had to pass all of them. No exceptions, no "we'll fix it later".&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="nl"&gt;"scripts"&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;"test:lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pint --test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:type-coverage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pest --type-coverage --exactly=100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:unit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pest --coverage --exactly=100"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"phpstan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test:refactor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rector --dry-run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&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="s2"&gt;"@test:lint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@test:type-coverage"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@test:unit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@test:types"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"@test:refactor"&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;This kept both me and my AI friend honest. And it compressed what might have been a week of work into a couple of days, which, given the usual client deadline situation, was exactly what I needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the package actually does
&lt;/h2&gt;

&lt;p&gt;The idea is simple: one command, one file, full database structure in a format your tools can immediately use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan er:generate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fwh4prvj28s4nztsmgzs7.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%2Fwh4prvj28s4nztsmgzs7.png" alt=" " width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are three main scenarios where this becomes useful.&lt;/p&gt;

&lt;p&gt;The first is giving AI agents database context without going insane. Instead of pasting migrations or manually describing tables at the start of every session, you generate a structured file that contains the full schema. The AI reads it once and has everything it needs — tables, columns, relationships, indexes, all in one place.&lt;/p&gt;

&lt;p&gt;The second is database documentation that doesn't require you to actually write documentation. In projects where the schema evolves frequently, having an always-up-to-date exportable reference is genuinely useful — for clients, for new team members, for yourself three months later when you've completely forgotten what that column is for.&lt;/p&gt;

&lt;p&gt;The third is visualizing complex schemas without any additional tooling. Mermaid and dbdiagram formats let you render a full ER diagram instantly, which is particularly helpful when onboarding on a legacy project or reviewing a PR that touches fifteen tables at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually learned from this
&lt;/h2&gt;

&lt;p&gt;Building a Laravel package is far less intimidating than it appears from the outside. The mechanics are straightforward once you understand how Composer autoloading and service provider discovery work. The harder part is thinking about how other people will use your code, not just how you use it yourself — that shift in perspective is where most of the design decisions live.&lt;/p&gt;

&lt;p&gt;The other thing that became very clear very quickly: spending time on architecture before writing code is not wasted time. Every feature I added after the initial structure was in place fit naturally because the extension points were already there. It's one of those things you hear constantly and don't fully believe until you experience it.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is just the beginning
&lt;/h2&gt;

&lt;p&gt;The package is not perfect, and I'm genuinely fine with that. The goal was never to ship a flawless tool on the first try. It was to understand how Laravel packages work from the inside, and to solve a real problem that was annoying me every single day.&lt;/p&gt;

&lt;p&gt;There are already many directions it could go from here: additional export formats, better diagram generation, deeper schema analysis, integration with more AI tools. Open source is also about learning from developers more experienced than you, and I'm genuinely happy to receive contributions, suggestions, or even just someone pointing out that I did something in a completely wrong way.&lt;/p&gt;

&lt;p&gt;If you want to try it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require paolobellini/laravel-er
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/paolobellini/laravel-er" rel="noopener noreferrer"&gt;https://github.com/paolobellini/laravel-er&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>laravel</category>
      <category>documentation</category>
    </item>
  </channel>
</rss>
