DEV Community

Paolo
Paolo

Posted on

I built a Laravel package to stop explaining my database to AI tools every single day

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.

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.

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.

Why a package, and why now

Two reasons pushed me over the edge.

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.

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.

So I opened a new repo, knowing that if I got stuck I could ask my friend Claude.

Designing before writing a single line

Before writing a single line of implementation, I did two things.

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.

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.

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.

The architecture

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.

Two design patterns ended up being a perfect fit for what I needed.

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.

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.

Using my friend Claude as a coding assistant

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.

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".

"scripts": {
    "test:lint": "pint --test",
    "test:type-coverage": "pest --type-coverage --exactly=100",
    "test:unit": "pest --coverage --exactly=100",
    "test:types": "phpstan",
    "test:refactor": "rector --dry-run",
    "test": [
        "@test:lint",
        "@test:type-coverage",
        "@test:unit",
        "@test:types",
        "@test:refactor"
    ]
}
Enter fullscreen mode Exit fullscreen mode

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.

What the package actually does

The idea is simple: one command, one file, full database structure in a format your tools can immediately use.

php artisan er:generate
Enter fullscreen mode Exit fullscreen mode

There are three main scenarios where this becomes useful.

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.

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.

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.

What I actually learned from this

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.

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.

This is just the beginning

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.

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.

If you want to try it:

composer require paolobellini/laravel-er
Enter fullscreen mode Exit fullscreen mode

https://github.com/paolobellini/laravel-er

Top comments (1)

Collapse
 
michiruf profile image
Michael Ruf

I havent digged into your tool yet, but from the first look, this really looks very useful! Thanks